\DocumentMetadata{}
\documentclass{cusdoc}
\usepackage{texhigh}
\ExplSyntaxOn\makeatletter
\sys_if_shell_unrestricted:T
  {
    \THSetClassCS[]{cus}{\mbox{\THcolor{blue!80!black}\bfseries
      \transparent{0.7}\texhigh@underline{\transparent{1}#1#2}}}
    \texhighsetclassfallback{cs}{cus.l}{cus, latex}
    \texhighsetclassfallback{cs}{cus.r}{cus, latex}
    \texhighsetclassfallback{cs}{cus.more}{cus, latex}
    \texhighsetclassfallback{cs}{ekeys}{cus, latex}
    \texhighsetclassfallback{cs}{fmulticol}{cus, latex}
    \cs_if_exist:NTF \xeCJKsetup
      {
        \THSaveStyle{xamplestyle}{
          \SetKeys[texhigh/high]{config-file+=cus-cn-texhigh.cfg,
            font=\xeCJKsetup{CJKecglue={\hskip 0pt plus 0.08\baselineskip}}\normalfont\ttfamily\small}
        }
      }
      {
        \THSaveStyle{xamplestyle}{
          \SetKeys[texhigh/high]{config-file+=cus-cn-texhigh.cfg, font=\normalfont\ttfamily\small}
        }
      }
    \RenewDocumentCommand{\xamplecodefile}{+m+m+m}
      {
        \IfBlankTF{#2}
          {\texhighfile[{#1}]{#3}\par\vskip-5pt plus 1pt minus 1pt\relax}
          {\lstinputlisting[#1,#2]{#3}}
      }
  }
\makeatother\ExplSyntaxOff

\hypersetup{pdfauthor={Longaster, \CusTeX},
  pdftitle=\CusTeX 宏集手册,
  pdfcreator={\XeLaTeX} with hyperref and \CusLaTeX}

\newsavebox\WaterMarkBox
\sbox{\WaterMarkBox}{\rotatebox{45}{\color{gray!30}\fontsize{100}{0}\sffamily \CusTeX}}
\background+[./watermark]{\copy\WaterMarkBox}

\newindextype[auto=true,filename=\jobname.idx,heading*={\section}]{\empty}
\setupindex[\empty,docchange]{auto=false}

\makeatletter

\setpagestyle*{fancy}[totalempty]{
  \sethead [ol,er] {\CusTeX 宏集手册}
  \sethead [or,el] {\headlink@warp\marked@title}
  % \setfoot [or,el] {\texttt{longaster@163.com}}
  \setfoot [ol,er] {第\thepage 页}
  \setheadrulewidth {1pt}
}

\protected\def\normalsize{%
  \@setfontsize \normalsize {10.53937}{12.64725}%
  \abovedisplayskip 1\p@ \@plus 4\p@ \@minus 2\p@ 
  \abovedisplayshortskip \z@ \@plus 2\p@ 
  \belowdisplayskip \abovedisplayskip 
  \belowdisplayshortskip \abovedisplayshortskip
  \let \@listi \@listI
}
\makeatother

\def\nofuncskip{\par\vskip-\bigskipamount\vskip\parskip\par}
\def\zhslash{／}
\newcommand\UNEXPANDEDRESULT{最终结果使用 \tn{unexpanded} （\cs{exp_not:n}）包裹起来。}

\raggedbottom \hfuzz=2.5pt \vfuzz=10pt 

\title{\CusTeX 宏集手册}
\author{Longaster}
\date{\zhtoday\quad v\UseName{cus@versi@n}}

\enablecombinedlist 

\begin{document}

\usepagestyle{totalempty}
\setlength{\lineskiplimit}{4pt}
\setlength{\lineskip}{4pt}

\def\thepage{t.\arabic{page}}
\setuplayout{preset=balance}
\maketitle

\frontmatter
\cusdoctoc

\mainmatter
\setuplayout{preset=main}
\usepagestyle{fancy}
\setuptitle[chapter]{numbering=true,pagestyle=fancy}
\removebackground[./watermark]


\chapter{概述}

{\color{red}\bfseries 目前 \CusTeX 还处于早期的开发状态中，很多功能还并不完善。}

\CusTeX （\CusLaTeX）宏集意为 a \textcolor{purple}Chinese \textcolor{purple}User
\textcolor{purple}Scheme \textcolor{purple}\TeX（\textcolor{purple}{\LaTeX}），
为中文 \LaTeX 用户定制的文档类框架。

对于排版外文文档，已经有诸如 \hologo{KOMAScript}、\cls{memoir} 等优秀的文档类，
由于中文文档的特殊性，直接使用它们虽然可能，但这些文档类终究不是为中文用户设计的，
使用起来仍有些不便。
而像 \pkg{ctex} 文档类，则注重解决输出中文的最根本的问题，
要求它们具有像 \hologo{KOMAScript} 文档类的完整功能不太可能。
如此，本宏集应运而生。

使用 \CusTeX 可以方便地设置标题、目录、页面样式（页面几何元素、页眉页脚等）、图表、背景、水印、
边注、脚注、列表、索引、术语表等文档元素，具有强大的可定制性。\CusTeX 原生兼容 \pkg{pgf} 和 
\pkg{tcolorbox}，加载这两个宏包或使用 \cuslibrary{pgf} 库可实现更多的功能 \TODO。

\CusTeX 通过模块（module）和库（library）来实现诸多功能。其中\emph{模块}是核心部分，
\CusTeX 将自动加载它们；库是提供额外功能的，用户可以选择是否加载它们。库可能依赖其它模块和库，
但模块不会依赖库。

模块和库均可能加载其它宏包，一般情况下，\CusTeX 会自动加载这些模块并处理好它们的依赖和兼容性，
当用户需要加载其它宏包时，最好通过 \CusTeX 的宏包加载机制来加载它们 \TODO。

\CusTeX 支持 \XeLaTeX、\LuaLaTeX、\upLaTeX、\ApLaTeX（p\LaTeX-ng）等多种编译方式，其中
\LuaLaTeX、\upLaTeX、\ApLaTeX 还支持竖排 \TODO。

\CusTeX 还很好的支持和适配了通用驱动（generic driver），这是 \LaTeXe 2022-06-01 中的新功能。

\emph{不}兼容 \pkg{beamer}。

\chapter{文档接口}

\CusTeX 定义的命令有的用于文档中，有的则是面向开发者，本章描述那些在文档中可能使用到的接口。

\begin{function}{\CusTeX,\CusLaTeX}
Logo。输出 \CusTeX，\CusLaTeX。
\end{function}

\begin{function}{\cussetup}
\begin{syntax}
  \verb|\cussetup| \marg{key-vals}
  \verb|\cussetup| \oarg{key path} \marg{key-vals}
  \verb|\cussetup| \{
  ~~\meta{key path_1} = \marg{key-vals_1} ,
  ~~\meta{key path_2} = \marg{key-vals_2} ,
  ~~...
  \}
\end{syntax}
键值设置命令。

\CusTeX 的不同模块使用不同的 \meta{key path}，一般情况下，这些模块会提供自己的键值设置接口，
为了使用 \cs{cussetup} 来设置这些键值，需要指定 \meta{key path}。
\end{function}

在本文档中，键的说明文字旁的表格中列出了键的完整写法，\meta{key path} 即为灰色的部分。
如键 \keyreflist[frame]{outer-sep,sep} 可以写成 
\begin{xample}
\cussetup[frame]{outer-sep=0pt, sep=20pt}
或 
\cussetup{ frame/outer-sep=0pt, frame/sep=20pt }
\stopxamplecode
\xamplecode\medskip
\end{xample}
有的键有默认值，就是说，当只写出键，而不写出等号以及后面的值时，自动的使用这个默认值。
默认值一般以加粗字体显示在键的后面，有时也会在键的说明中给出。

初始值是在键定义时就赋给键的值，只执行一次，如果没有修改过这个键，或者只在局部修改它，
那么在其它地方都使用这个初始值。

还有的键会根据其它设置而自动修改，根据实际需要和作用，每个键有可能有特殊的用法，
阅读相应说明即可。

\begin{function}{\cussetstyle}
\begin{syntax}
  \verb|\cussetstyle|   \oarg{key path} \marg{key} \marg{key-vals}
  \verb|\cussetstyle| * \oarg{key path} \marg{key} \marg{code}
\end{syntax}
自定义键。

带 \verb|*| 的可使用一个参数，它代表键传入的值。
\end{function}

例如，若要为 \envref{Framed} 新增键选项，则可以使用此命令。
\begin{xample}
\colorlet{frame color}{black}
\colorlet{fill color}{white}
\cussetstyle*[frame]{frame color}{\colorlet{frame color}{#1}}
\cussetstyle*[frame]{fill color}{\colorlet{fill color}{#1}}
\cussetstyle[frame]{color frame}{%
  frame={\setlength{\fboxsep}{#1\cusframesep}%
    \setlength{\fboxrule}{#1\cusframerule}%
    \fcolorbox{frame color}{fill color}}%
}
\begin{Framed}[frame color=blue, fill color=blue!20, color frame=2]
  \zhlipsum[1]
\end{Framed}
\stopxamplecode
\xamplecode\medskip
\end{xample}

本说明文档中，若参数是 \meta{clist} 或 \meta{comma list}，则说明该参数是逗号分隔的列表；
若是键值选项，说明参数是逗号分隔的键值选项列表；若是 \meta{list}，则一般是普通的记号列表，
如果一项有多个记号，需要把这些记号用 \verb|{}| 括起来。

\section{\cusmodule{ltx}模块}

\cusmodule{ltx} 模块，本模块封装或提供一些 \LaTeXe 的接口。

\begin{function}[EXP]{\csstring}
  \begin{syntax}
    \V\csstring \meta{cs}
  \end{syntax}
提取控制序列 \meta{cs} 的名字。
\end{function}

\begin{xample}
\ttfamily \csstring\CusTeX \quad \csstring\|
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\Replicate}
\begin{syntax}
  \verb|\Replicate| \marg{num expr} \marg{code}
\end{syntax}
重复 \meta{code} \meta{num expr} 次。
\end{function}

\begin{function}{\lo,\hi,\lohi} 
  \begin{syntax}
    \V\lo   \marg{material}
    \V\hi   \marg{material}
    \V\lohi \marg{lo material} \marg{hi material}
  \end{syntax}
在数学模式中，它们相当于 \verb|{}_{...}|、\verb|{}^{...}|、\verb|_{...}^{...}|，
在文本模式中，它们也可直接使用，\cs{lo} 相当于 \tn{textsubscript}，
\cs{hi} 相当于 \tn{textsuperscript}。
\end{function}

\begin{xample}
\Large 字\lo{下}\hi{上}，字\lohi{下}{上}。$ H\lo{u}\hi{n} H\lohi{u}{n}$.
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\makelapbox,\parlapbox}
  \begin{syntax}
    \V\makelapbox \marg{text}
    \V\makelapbox \oarg{width} \oarg{pos} \marg{text}
    \V\makelapbox \oarg{width} \oarg{pos} \oarg{lap to} \marg{text}
    \V\parlapbox  \marg{width} \marg{text}
    \V\parlapbox  \oarg{pos} \oarg{height} \oarg{inner-pos} \marg{width} \marg{text}
    \V\parlapbox  \oarg{pos} \oarg{height} \oarg{inner-pos} \oarg{lap to} \marg{width} \marg{text}
  \end{syntax}

\cs{makelapbox} 的用法和 \tn{makebox} 一样，但是会把它向 \meta{lap to} 侧重叠。
若给出了 \meta{width} 或它不为空，则先把 \meta{text} 放在宽为 \meta{width} 的盒子中。
\meta{pos} 可选值为 \UseList[\texttt]{lcr}{、}，
分别表示把 \meta{text} 放在盒子的左边、中间、右边，默认值为 \texttt{c}。
\meta{lap to} 的可选值为 \UseList[\texttt]{lcr}{、}，
分别表示把 \meta{text}（如果有 \meta{width}，则是把宽为 \meta{width} 的盒子）嵌入
到左、右的文字中或一半嵌入到左边一半嵌入到右边，\meta{lap to} 的默认值为 \meta{pos} 的值。

\cs{parlapbox} 的用法和 \tn{parbox} 一样，但是会把它向 \meta{lap to} 侧重叠。
\meta{pos} 指定基线的位置，可选值为 \UseList[\texttt]{tcb}{、}，
分别表示第一行文字的基线位置、文字的中间以及末行文字的基线。的默认值为 \texttt{c}。
若给出 \texttt{height}，则把文字放在高度为 \texttt{height} 的盒子中，
根据 \meta{inner-pos} 来决定文字的垂直位置，可选值为 \UseList[\texttt]{tcb}{、}。
\end{function}

\begin{xample}
这里\makelapbox{是}文字。
这里\makelapbox[][l]{是}文字。
这里\makelapbox[][r]{是}文字。
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[rEXP]{\numberfixedwidth,\numberzerofill}
  \begin{syntax}
    \V\numberfixedwidth \marg{width} \marg{filler} \marg{printer} \marg{number}
    \V\numberzerofill \marg{width} \marg{number}
  \end{syntax}
先将 \meta{printer} 作用于 \meta{number}，然后用 \meta{filler} 向左或向右填充，
填充 \meta{filler} 的次数为 $\veta{width}-\mathrm{len}(\veta{printer}(\meta{number}))$。
当 \meta{width} 小于 0 时，在右边填充，否则在左边填充。
\meta{printer} 必须是可展的。

\cs{numberzerofill} 用 0 填充数字。
\end{function}

\begin{xample}
\ExplSyntaxOn
\numberfixedwidth { 6 } { 0 } { \int_to_Hex:n } { 42 } \quad 
\numberfixedwidth { 6 } { 0 } { \int_to_Hex:n } { `好 } \quad 
\numberzerofill { 6 } { 12478 }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\zkern}
相当于 \verb|\kern 0pt\relax|。
\end{function}

\begin{function}[type=environment]{enumlist,enumlist*}
  \begin{syntax}
    \verb|\begin{enumlist}| \oarg{default label} \marg{left} \marg{indent} \marg{label sep} \marg{right}
    ~~~~...
    \verb|\end{enumlist}|
  \end{syntax}
相当于 \env{list} 环境。

\meta{default label} 为列表的标签，默认为空；\meta{left} 为左侧间距；
\meta{indent} 为每段首行的缩进；\meta{label sep} 为标签与首行的间距；
\meta{right} 为右侧间距。

带星号的环境还会设置段落间距和每项的间距为 0pt。
\end{function}

\begin{function}{\cusemoji,\cusemojitotalratio,\cusemojilowerratio}
  \begin{syntax}
    \V\cusemoji \marg{pic filename}
  \end{syntax}
插入一张图片，它的（总）高度为当前文字的高度的 \cs{cusemojitotalratio} 倍，
并向下移动 \cs{cusemojilowerratio} 个文字的高度。
\cs{cusemojitotalratio} 和 \cs{cusemojilowerratio} 
必须为 \texttt{0} 或 \veta{a}\texttt/\veta{b}，其中 \veta{a}、
\veta{b} 为非零整数。
\cs{cusemojitotal\-ratio} 默认为 \texttt{8/9}，如果是负数则上下翻转，但宽度也会变成负值。
\cs{cusemoji\-lowerratio} 默认为 \texttt{1/7}，如果是负数则向上移动。

可以用于数学模式，会根据是否处于上下标而改变大小。

需要用户自行加载 \pkg{graphicx} 宏包。
\end{function}

\begin{xample}
\newcommand{\bdhj}{\cusemoji{bd-huaji.png}}
滑稽 \bdhj ； $ \sin\bdhj = 2^\bdhj $
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\IfGraphicsExists}
  \begin{syntax}
    \V\IfGraphicsExists \marg{graphics name} \marg{true code} \marg{false code}
  \end{syntax}
判断图片文件是否存在。\tn{@curr@file} 展开为此文件名，若无此文件则为 \tn{relax}。

它会自动查找 \tn{setgraphicspath} 设置的路径，且可以自动补全文件扩展名。

需要用户自行加载 \pkg{graphicx} 宏包。
\end{function}

\begin{function}{\InputIfGraphicsExists}
  \begin{syntax}
    \V\InputIfGraphicsExists * \oarg{key-val list} \marg{file}
  \end{syntax}
如果图片存在时使用图片，否则什么也不做。

它会自动查找 \tn{setgraphicspath} 设置的路径，且可以自动补全文件扩展名。

需要用户自行加载 \pkg{graphicx} 宏包。
\end{function}

\begin{function}{\setinputpath,\setgraphicspath}
  \begin{syntax}
    \V\setinputpath      \marg{path clist}
    \V\setinputpath    + \marg{path clist}
    \V\setgraphicspath   \marg{path clist}
    \V\setgraphicspath + \marg{path clist}
  \end{syntax}
设置导入文件或导入图片时需要查找的路径。\meta{path clist} 使用逗号分隔，
且需使用 \texttt/ 作为目录分隔符。带 \texttt+ 的命令为附加到原有的设置之后。

使用 \tn{setgraphicspath} 时需要自行加载 \pkg{graphicx} 宏包。
\end{function}

\subsection{参数处理器，Argument processors}

\pkg{ltcmd} 提供了 \cs{NewDocumentCommand} 等命令来定义新的命令，每个参数可以使用
“参数处理器”来先行处理，再传递给实际的代码（或其它参数处理器），并提供了 \cs{Reverse\-Bool\-ean}、
\cmd\SplitArgument、\cmd\SplitList、\cs{TrimSpaces} 等几个参数处理器。

本模块提供了更多的处理器。

\begin{function}{\ReplaceArgumentIf}
  \begin{syntax}
    \V\ReplaceArgumentIf \marg{test function} \marg{true replacement} \marg{false replacement}
  \end{syntax}
\meta{test function} 需要三个参数，分别为要测试的值、true 分支、false 分支。
当测试为真时，把参数替换为 \meta{true replacement}，否则，替换为 \meta{false replacement}。
\end{function}

\begin{xample}
\newcommand\mytestfake[3]{\ifthenelse{\equal{#1}{fake}}{#2}{#3}}
%\usepackage{ifthen}
\DeclareDocumentCommand\whatnews
  { >{ \ReplaceArgumentIf{\mytestfake}{true}{#1} } m }
  {#1 news}
\whatnews{fake}, \whatnews{some}.
\stopxamplecode
\xampleprint
\end{xample}

上述代码定义了一个命令 \verb|\whatnews|，它检查第一个参数是否为 \verb|fake|，如果是，
则替换为 \verb|true|。

\begin{function}{\ReplaceArgumentIfEqual,\ReplaceArgumentIfStrEqual}
  \begin{syntax}
    \V\ReplaceArgumentIfEqual    \marg{tl} \marg{true replacement} \marg{false replacement}
    \V\ReplaceArgumentIfStrEqual \marg{str} \marg{true replacement} \marg{false replacement}
  \end{syntax}
判断参数是否等于 \meta{tl}（或 \meta{str}），如果是则替换为 \meta{true replacement}，否则，
替换为 \meta{false replacement}。
\end{function}

\begin{xample}
\DeclareDocumentCommand\foo
  { >{ \ReplaceArgumentIfEqual{s}{c}{#2} } m 
    >{ \ReplaceArgumentIfEqual{j}{m}{#2} } m }
  {[#1][#2]}

\foo {k}{j}
\foo {s}{o}
\stopxamplecode
\xampleprint
\end{xample}

上述代码定义了一个命令 \verb|\foo|，它判断第一个参数是否为 \verb|s|，如果是，
则替换为 \verb|c|，否则替换为第二个参数的值（在使用它的参数处理器之前的值）。
判断第二个参数是否为 \verb|j|，如果是则替换为 \verb|m|，否则不变。

\begin{function}{\ReplaceArgumentIfMatch}
  \begin{syntax}
    \V\ReplaceArgumentIfMatch \marg{regex} \marg{true replacement} \marg{false replacement}
  \end{syntax}
判断此参数是否匹配正则表达式 \meta{regex}，如果是，则替换为 \meta{true replacement}，
否则，替换为 \meta{false replacement}。
\end{function}

\begin{function}{\ExpandArgument}
  \begin{syntax}
    \V\ExpandArgument \marg{spec}
  \end{syntax}
类似于 \cs{ExpandArgs}，先使用 \meta{spec} 指定的展开方式展开这个参数，再传递给实际的代码（或其它参数处理器）。

目前有效的 \meta{spec} 为 \verb|coVvefx| 和 \verb|p| 之一。前几个和 \cs{ExpandArgs}
的类似，\verb|p| 类似于 \verb|x|，但那些被保护的命令和未定义的命令以及
数学公式中的命令不会被展开。

此外，还有几个特殊的 spec：
\begin{itemize}
  \item[\texttt{sS}] --- 把参数转化为字符串，（使用 \tn{detokenize}）；
  \item[\texttt{sX}] --- 和 \verb|p| 完全一样，（使用 \cs{text_expand:n}）；
  \item[\texttt{sF}] --- 类似于 \verb|x|，但不可展开的记号和未定义的命令被移除了，（使用 \cs{text_purify:n}）；
  \item[\texttt{sP}] --- 类似于 \verb|sX|，速度更快，但数学公式中的命令会被展开，未定义的命令也会出错。
\end{itemize}
\end{function}

\begin{xample}
\DeclareDocumentCommand \faa 
  {                        >{\ExpandArgument{p}}  m }{#1}
\DeclareDocumentCommand \fee 
  { >{\ExpandArgument{sS}} >{\ExpandArgument{p}}  m }{#1}
\DeclareDocumentCommand \fii 
  { >{\ExpandArgument{sS}} >{\ExpandArgument{sP}} m }{#1}
\DeclareDocumentCommand \foo 
  { >{\ExpandArgument{sS}} >{\ExpandArgument{sF}} m }{#1}
\newcommand{\mytextit}[1]{\textit{#1}}
\faa{\textbf{bfseries} \mytextit{itshape} $ \mytextit{math rm } a+b=c $}\par 
\ttfamily
\fee{\textbf{bfseries} \mytextit{itshape} $ \mytextit{math rm } a+b=c $}\par 
\fii{\textbf{bfseries} \mytextit{itshape} $ \mytextit{math rm } a+b=c $}\par 
\foo{\textbf{bfseries} \mytextit{itshape} $ \mytextit{math rm } a+b=c $}\par 
\stopxamplecode
\xampleprint
\end{xample}

\begin{xample}
\DeclareDocumentCommand\oof
  { >{ \ReplaceArgumentIfMatch{\A.\Z}{0#1}{#1} } >{ \ExpandArgument{e} } m 
    >{ \ReplaceArgumentIfMatch{(.{2,}|[^lcr])}{c}{#2} } m }
  {[#1][#2]}

\oof {1}{m}
\oof {10}{mn}
\oof {jk}{r}
\oof {{jk}}{r}
\DeclareDocumentCommand\mytext{}{ab}% 不能被展开
\oof {\mytext}{m}
\DeclareExpandableDocumentCommand\mytext{}{ab}% 可以被展开
\oof {\mytext}{m}
\stopxamplecode
\xampleprint
\end{xample}

上述代码定义了一个命令 \verb|\oof|，它的第一个参数先被完全展开
（使用 \cs{Exp\-and\-Argument}），
再传递给后一个参数处理器，这个参数处理器判断此参数是否是单个记号，如果是，则在其左侧加上 
\verb|0|，否则保持不变。

它的第二个参数使用正则表达式 \verb!(.{2,}|[^lcr])! 进行判断，如果匹配则替换为 \verb|c|，
否则保持不变。

\begin{function}{\RegexReplaceArgument}
  \begin{syntax}
    \V\RegexReplaceArgument   \marg{regex} \marg{regex replacement}
    \V\RegexReplaceArgument + \marg{regex} \marg{regex replacement}
  \end{syntax}
在参数中使用 \meta{regex} 查找，并用 \meta{replacement} 替换之。

带 \texttt{+} 的替换所有，不带 \texttt{+} 的替换一次。
\end{function}

\begin{xample}
\DeclareDocumentCommand\foo
  { >{ \RegexReplaceArgument
        {(\d{2,4})[\/\-](\d{1,2})[\/\-](\d{1,2})}
        {\1/\2/\3}
     }
    m }
  {#1}
\foo{1920/02/09}
\foo{1920-02-09}
\stopxamplecode
\xampleprint
\end{xample}

\begin{texnote}
以上这些正则表达式的匹配和替换使用的是 \LaTeXiii 的 \texttt{l3regex} 库中的命令，
如 \cs{regex_match:nnTF}、\cs{regex_replace_once:nnN}、\cs{regex_replace_all:nnN}，
支持的正则表达式语法请参考 \file{interface3.pdf}。
\end{texnote}

\begin{keyval}[path=typo]{special-dischyph,dischyph-opacity}
  \begin{syntax}
    special-dischyph = <&normal|opacity|none>
    dischyph-opacity = <{0--1之间的数}>
  \end{syntax}
\verb|special-dischyph| 可以让 \tn{-} （和 \tn{@dischyph}）显示的文字具有透明度。
\opt{normal} 为默认的显示效果。\opt{none} 移除显示的字符。

\opt{dischyph-opacity} 设置透明度，但不会直接修改 \opt{special-dischyph}。

使用 \cs{DocumentMetadata} 后效果更好。
\end{keyval}


\section{\cusmodule{util}模块}

\nofuncskip
\begin{function}[rEXP]{\MapClist,\MapList,\MapInteger}
\begin{syntax}
  \V\MapClist \marg{comma list} \marg{tokens}
  \V\MapList  \marg{list} \marg{tokens}
  \V\MapInteger \oarg{initial value} \oarg{step} \marg{final value} \meta{function}
\end{syntax}
\cs{MapClist} 使用 \meta{tokens} 迭代逗号分隔的列表 \meta{comma list}，
它将 \meta{tokens} 置于列表项之前。

\cs{MapList} 使用 \meta{tokens} 迭代记号列表 \meta{list}，它将 \meta{tokens} 置于列表项之前。

\cs{MapInteger} 从 \meta{initial value} 到 \meta{final value} 以 \meta{step} 为步长，
来迭代 \meta{function}。
\end{function}

\begin{function}{\IterateClist,\IterateList,\IterateInteger}
\begin{syntax}
  \V\IterateClist \marg{comma list} \marg{inline code}
  \V\IterateList  \marg{list} \marg{inline code}
  \V\IterateInteger \oarg{initial value} \oarg{step} \marg{final value} \marg{inline code}
\end{syntax}
\cs{IterateClist} 使用 \meta{inline code} 迭代逗号分隔的列表 \meta{comma list}，\meta{inline code} 可带一个参数 \verb|#1|，它为当前迭代项。

\cs{IterateList} 使用 \meta{inline code} 迭代记号列表 \meta{list}，\meta{inline code} 可带一个参数 \verb|#1|，它为当前迭代项。

\cs{IterateInteger} 从 \meta{initial value} 到 \meta{final value} 以 \meta{step} 为步
长，来迭代 \meta{inline code}，\meta{inline code} 可以带一个参数 \verb|#1|，它为当前整数。
\end{function}

\begin{xample}
$ \MapClist{1,2,3,n}{a_} $ \quad $ \IterateClist{1,2,3}{a_{#1}+} a_n $
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\IterateThread}
  \begin{syntax}
    \verb|\IterateThread|   \marg{comma list_1} \marg{comma list_2} \marg{inline code}
    \verb|\IterateThread| * \marg{comma list_1} \marg{comma list_2} \marg{inline code}
    \verb|\IterateThread|   \oarg{n} \marg{comma list_1} ... \marg{comma list_n} \marg{inline code}
    \verb|\IterateThread| * \oarg{n} \marg{comma list_1} ... \marg{comma list_n} \marg{inline code}
    \verb|\IterateThread|   \oarg{n} \marg{comma list_1} ... \marg{comma list_n} 
    ~~~~~~~~~~~~~~~~~\oarg{middle} \marg{inline code}
    \verb|\IterateThread|   \oarg{n} \marg{comma list_1} ... \marg{comma list_n} 
    ~~~~~~~~~~~~~~~~~\oarg{middle} \oarg{last} \marg{inline code}
    \verb|\IterateThread| * \oarg{n} \marg{comma list_1} ... \marg{comma list_n} 
    ~~~~~~~~~~~~~~~~~\oarg{middle} \oarg{last} \marg{inline code}
  \end{syntax}
使用 \meta{inline code} 迭代这 $n$ 个 \meta{comma list}，\meta{inline code} 
可接受 $n+1$ 个参数，其中第一个参数为索引，其后的参数分别为诸列表的当前迭代项。
当某一个列表结束时迭代终止，多余的项被移除。$n$ 的可选值为 1 -- 7，即最多可使用 7 个列表。

使用 \meta{middle} 来分隔各项，最后两项用 \meta{last} 分隔，默认与 \meta{middle} 一致。如未给出，则为空，即不在两项之间插入其它符号。

带 \verb|*| 的版本保留空项和每项前后的空格，不带 \verb|*| 的则不保留。

若某个 \meta{comma list} 为单个记号，则将其展开一次。这样，可以使用一个宏保存列表项。
\end{function}

\begin{xample}
$ \IterateThread{a+b,c+d,e+f}{A+B,C+D,E+F}{\dfrac{#2}{#3}\geq} 0 $ \par 
$ \IterateThread  {a+b, ,e+f}{A+B,C+D, }{\dfrac{#2}{#3}\geq} 0 $ \par 
$ \IterateThread *{a+b, ,e+f}{A+B,C+D, }{\dfrac{#2}{#3}\geq} 0 $ \par 
\stopxamplecode
\xamplecode
\xampleline\par\smallskip
\xampletext
\end{xample}

\begin{xample}
$ \IterateThread[2]{1,2,3,n}{n,n-1,n-2,1}[+][+\cdots+]{a_{#2}\cdot b^{#3}} $
%= $ a_1\cdot b^n+a_2\cdot b^{n-1}+a_3\cdot b^{n-2}+\cdots+a_n\cdot b^1 $
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\UseList,\UseClist}
  \begin{syntax}
    \V\UseList \marg{token list} \marg{separator}
    \V\UseList \oarg{function} \marg{token list} \oarg{final two separator} \marg{separator}
    \V\UseClist \marg{comma list} \marg{separator}
    \V\UseClist \oarg{function} \marg{comma list} \oarg{final two separator} \marg{separator}
  \end{syntax}
把 \meta{separator} 插入到 \meta{token list}（或 \meta{comma list}）中间。

\meta{function} 会应用到 \meta{token list}（\meta{comma list}）的每一项。
当只有两项时，这两项中间会使用 \meta{final two separator}；
最后两项之间也会使用 \meta{final two separator}，其它情况的项中间使用 \meta{separator}。
\end{function}

\begin{xample}
\UseList[\texttt]{abc}{、}。
\UseList[\texttt]{ab}[和]{、}。
\UseList[\texttt]{abcd}[和]{、}。
\UseClist[\texttt]{a,b,c}{、}。
\UseClist[\texttt]{a,b}[和]{、}。
\UseClist[\texttt]{a,b,c,d}[和]{、}。
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[rEXP]{\ucchar,\ucchars}
  \begin{syntax}
    \verb|\ucchar|  \marg{unicode slot}
    \verb|\ucchars| \marg{unicode slots}
  \end{syntax}
展开为 \meta{unicode slot} 对应的 Unicode 字符。\meta{unicode slots} 为空格分隔的 
Unicode 代码点。
\end{function}

\begin{xample}
\ucchar{"5982}：%
\ucchars{"75 "74 "69 "6C "6A21 "5757}。
\stopxamplecode 
\xampleprint
\end{xample}

\begin{function}{\Verbatimize}
  \begin{syntax}
    \verb|\Verbatimize|   \marg{balanced tokens}
    \verb|\Verbatimize| * \meta{token} \meta{tokens} \meta{token}
  \end{syntax}
以 verbatim 的形式输出 \meta{balanced tokens} 或 \meta{tokens}。

带 \verb|*| 的版本作用与 \tn{verb} 类似，由一对 \meta{token} 包裹，也支持一对 \verb|{ }| 包裹。只是它仍然使用当前字体。不能作为一个命令的参数。

不带 \verb|*| 的版本可以作为另一个命令的参数，但如下几个字符必须使用转义的形式：
\texttt{\Verbatimize{\#\$\%\ \{\}\\\^}}，即，使用 \verb|\#\$\%\ \{\}\\\^|。
\end{function}

\begin{function}{\IfPageOdd,\IfAbsPageOdd}
  \begin{syntax}
    \verb|\IfPageOdd| \marg{true} \marg{false}
  \end{syntax}
判断当前页码是否为奇数。\cs{IfAbsPageOdd} 仅在 shipout 时有效（如在 \hook{shipout/\allowbreak foreground}，\hook{shipout/background}，\hook{shipout}，
\hook{shipout/after} 钩子中）。

平常使用时并不一定准确，\cusmodule{ref} 库改进了这一点，见\cref{sec:lib-ref}。
\end{function}

\begin{function}[EXP]{\@ifpageodd,\@ifabspageodd}
同上，但可展。
\end{function}


\section{\cusmodule{space}模块}

\cusmodule{space} 模块提供与空白间距有关的接口。

\begin{function}{\needspace,\Needspace,\NeedspaceTF}
  \begin{syntax}
    \V\needspace   \marg{dim expr}
    \V\Needspace   \marg{dim expr}
    \V\Needspace * \marg{dim expr}
    \V\NeedspaceTF \marg{dim expr} \marg{true code} \marg{false code}
    \V\NeedspaceTF*\marg{dim expr} \marg{true code} \marg{false code}
  \end{syntax}
判断当前页的版心是否至少留有 \meta{dim expr} 的垂直空白。
\cs{needspace} 为粗略估算，
\cs{Needspace} 和 \cs{NeedspaceTF} 则是要求必须至少留有 \meta{dim expr} 的空白。
\cs{NeedspaceTF} 会在需要分页（或已经是新的一页）时使用 \meta{truec code}，
反之则使用 \meta{false code}。

带星号的 \cs{Needspace} 和 \cs{NeedspaceTF} 会使得 \tn{flushbuttom} 生效。

它们只在主垂直列中有效。
\end{function}

\begin{function}{\needhspace}
  \begin{syntax}
    \V\needhspace \marg{dim expr}
  \end{syntax}
要求当前行至少留有 \meta{dim expr} 的间距，为粗略估算。若要严格的值，可参考 \pkg{tab} 宏包。

只在垂直模式中有效。
\end{function}

\begin{function}{\breakablevspace,\breakablehspace}
  \begin{syntax}
    \V\breakablevspace \marg{skip expr}
  \end{syntax}
留下 \meta{skip expr} 的垂直或水平空白，为粗略值，可断行和分页。
\end{function}


\section[float-barrier=on]{页面布局，\cusmodule{layout}模块}

\cusmodule{layout} 提供页面布局的相关接口。

\begin{function}{\setuplayout}
  \begin{syntax}
    \verb|\setuplayout|                \;\marg{layout key-val}
    \verb|\setuplayout|   \oarg{preset name} \marg{layout key-val}
    \verb|\setuplayout| * \oarg{preset name} \marg{layout key-val}
  \end{syntax}
设置布局。

第一个用法为直接设置页面布局。
第二个除了设置布局外，还将这个布局保存下来，可供后续重复使用。
第三个则仅保存布局，而不设置这个布局。

可以在文档中间改变布局，纸张大小也可改变。
\end{function}

% from geometry.dtx
\def\gpart#1{\textsf{\textsl{\color[rgb]{.0,.45,.7}#1}}}
% \thisfloatsetup{margins=hangoutside,capposition=beside,
% capbesideposition={top,inside},floatwidth=\textwidth}
\begin{figure}
 \IfPageOdd{\raggedleft}{\raggedright}\small
 {\unitlength=.65pt
 \begin{picture}(450,250)(0,-10)
 \put(20,0){\framebox(170,230){}}
 \put(20,235){\makebox(170,230)[br]{\gpart{paper}}}
 \begingroup\thicklines
 \put(40,30){\framebox(120,170){}}\endgroup
 \put(40,30){\makebox(120,165)[tr]{\gpart{total body}~}}
 \put(45,30){\makebox(0,170)[l]{\texttt{height}}}
 \put(40,35){\makebox(120,0)[bc]{\texttt{width}}}
 \put(50,-20){\makebox(120,0)[bc]{\texttt{paperwidth}}}
 \put(10,45){\makebox(0,170)[r]{\texttt{paperheight}}}
 \put(90,200){\makebox(0,30)[lc]{\texttt{top}}}
 \put(90,0){\makebox(0,30)[lc]{\texttt{bottom}}}
 \put(10,70){\makebox(0,0)[r]{\texttt{left}}}
 \put(10,55){\makebox(0,0)[r]{(\texttt{inner})}}
 \put(200,70){\makebox(0,0)[l]{\texttt{right}}}
 \put(200,55){\makebox(0,0)[l]{(\texttt{outer})}}
 \put(80,230){\vector(0,-1){30}}\put(80,30){\vector(0,-1){30}}
 \put(80,200){\vector(0,1){30}}\put(80,0){\vector(0,1){30}}
 \put(20,70){\vector(1,0){20}}\put(40,70){\vector(-1,0){20}}
 \put(160,70){\vector(1,0){30}}\put(190,70){\vector(-1,0){30}}
 \multiput(160,30)(5,0){24}{\line(1,0){2}}
 \multiput(160,200)(5,0){24}{\line(1,0){2}}
 \begingroup\thicklines
 \put(280,30){\framebox(120,170){}}\endgroup
 \put(283,133){\makebox(0,12)[l]{\texttt{textheight}}}
 \put(295,130){\vector(0,-1){100}}\put(295,150){\vector(0,1){50}}
 \multiput(280,220)(5,0){24}{\line(1,0){3}}
 \put(280,208){\makebox(120,20)[bc]{\gpart{head}}}
 \multiput(280,207)(5,0){24}{\line(1,0){3}}
 \put(420,225){\makebox(0,0)[l]{\texttt{headheight}}}
 \put(418,225){\line(-2,-1){20}}
 \put(420,213){\makebox(0,0)[l]{\texttt{headsep}}}
 \put(418,213){\line(-2,-1){20}}
 \put(420,12){\makebox(0,0)[l]{\texttt{footskip}}}
 \put(418,12){\line(-2,1){20}}
 \put(280,40){\makebox(120,140)[c]{\gpart{body}}}
 \put(305,45){\vector(-1,0){25}}\put(375,45){\vector(1,0){25}}
 \put(80,230){\vector(0,-1){30}}\put(80,30){\vector(0,-1){30}}
 \put(280,48){\makebox(120,0)[c]{\texttt{textwidth}}}
 \put(280,15){\makebox(120,10)[c]{\gpart{foot}}}
 \multiput(280,14)(5,0){24}{\line(1,0){2}}
 \put(410,30){\dashbox{3}(30,170){}}
 \put(415,30){\makebox(30,170)[l]{\gpart{marginal note}}}
 \put(425,45){\vector(-1,0){15}}\put(425,45){\vector(1,0){15}}
 \put(450,70){\makebox(0,0)[l]{\texttt{marginparsep}}}
 \put(448,70){\line(-3,-1){43}}
 \put(450,45){\makebox(0,0)[l]{\texttt{marginparwidth}}}
 \end{picture}}
 \caption{长度变量}
 \label{fig:layout}
\end{figure}


键值接口大都直接使用 \pkg{geometry} 宏包的接口。具体用法说明可参见其说明文档。如未作说明，
则与 \pkg{geometry} 宏包提供的接口用法相同。

\subsection{页面尺寸}

\nofuncskip 
\begin{keyval}[path=layout]{papername,paper}
  \begin{syntax}
    papername|paper = \marg{papername}
  \end{syntax}
设置纸张大小。\meta{papername} 为预定义的纸张名，大小写无关。
\end{keyval}

\begin{table}[tb]
  \IfPageOdd{\makebox[\linewidth][l]}{\makebox[\linewidth][r]}%
    {\includegraphics{tab-defined-papername}}
  \caption{预定义的纸张名}\label{tab:defined-papername}
\end{table}

\begin{keyval}[path=layout]{papersize,paperwidth,paperheight}
  \begin{syntax}
    papersize   = \{\meta{宽},\meta{高}\} 或 \{\meta{宽}:\meta{高}\} 或 \marg{长度}
    paperwidth  = \marg{宽}
    paperheight = \marg{高}
  \end{syntax}
设置纸张大小。
\end{keyval}

\begin{keyval}[path=layout]{paperorientation,orientation,landscape,portrait,direction}
  \begin{syntax}
    paperorientation|orientation = <&landscape|portrait>
    landscape &&
    portrait  &&
    direction = <&bigwidth|bigheight|normal|inverse>
  \end{syntax}
设置纸张方向。使用 \opt{portrait} 时，纸张高度大于宽度。\opt{landscape} 则反之。

\opt{direction} 的 \opt{bigheight} 和 \opt{normal} 相当于 \opt{portrait}，
\opt{bigwidth} 和 \opt{inverse} 相当于 \opt{landscape}。

使用 \opt{papername} 等选项时，将自动设置纸张方向，使得实际纸张宽高与所给一致。
\end{keyval}

\begin{keyval}[path=layout]{layout,layoutname,
  layoutwidth,layoutheight,layoutsize,
  layouthoffset,layoutvoffset,layoutoffset,centerlayout}
设置 \gpart{layout} 部分大小。

\opt{layout} 或 \opt{layoutname} 会根据纸张方向自动交换长宽，因此纸张方向必须先于它们设置。

\opt{centerlayout} 通过将 \opt{layoutfoffset} 和 \opt{layoutvoffset} 
设置为合适的值，以将 
\gpart{layout} 部分置于纸张中心。

见 \pkgdoc{geometry}。
\end{keyval}

\begin{function}{\layoutwidth,\layoutheight,
  \layoutloffset,\layouttoffset,\layoutroffset,\layoutboffset}
\gpart{layout} 的大小，以及距离纸张左、上、右、下侧的长度。在 \TikZ 中使用时可能需要用 
\tn{the} 获取它们的值，
使用 \cs{dimeval}、\tn{dimexpr} 等命令以及 \pkg{calc} 宏包的功能时则不需要。

若是设置了 \opt{twoside}，它们\emph{不会}自动切换左侧和右侧的值。
\end{function}

\subsection{主体尺寸}

此小节与 \pkg{geometry} 对应部分的用法和作用相同。

\begin{keyval}[path=layout]{hscale,vscale,scale}
  \begin{syntax}
    hscale = \marg{正实数} & 0.7
    vscale = \marg{正实数} & 0.7
    scale  = \{\meta{hscale},\meta{vscale}\} 或 \marg{正实数}
  \end{syntax}
设置 \gpart{total part} 部分的宽高与 纸张宽高的比率。
\end{keyval}

\begin{keyval}[path=layout]{totalwidth,width,totalheight,height,total}
  \begin{syntax}
    totalwidth |width  = \marg{长度}
    totalheight|height = \marg{长度}
    total              = \{\meta{totalwidth},\meta{totalheight}\} 或 \marg{长度}
  \end{syntax}
设置 \gpart{total part} 部分的宽高。
\end{keyval}

\begin{keyval}[path=layout]{textwidth,textheight,body,text}
  \begin{syntax}
    textwidth  = \marg{长度}
    textheight = \marg{长度}
    body       = \{\meta{textwidth},\meta{textheight}\}
    text       = \marg{长度}
  \end{syntax}
设置 \tn{textwidth}、\tn{textheight}，即 \gpart{body} 部分的宽高。
\end{keyval}

\begin{keyval}[path=layout]{lines}
  \begin{syntax}
    lines = \marg{行数}
  \end{syntax}
根据 \meta{行数} 设置 \opt{textheight}。\meta{行数} 一般为正整数。
\end{keyval}

\begin{keyval}[path=layout]{includehead,includefoot,
  includeheadfoot,includehf}
  \begin{syntax}
    includehead = <&\TTF> & false 
    includefoot = <&\TTF> & false
    includeheadfoot|includehf = <&\TTF>
  \end{syntax}
控制是否将页眉（\tn{headheight}、\tn{headsep}）、页脚（\tn{footskip}）
计入 \gpart{total part} 部分中。
\end{keyval}

\begin{keyval}[path=layout]{includemarginpar,includemp}
  \begin{syntax}
    includemarginpar|includemp = <&\TTF> & false 
  \end{syntax}
控制是否将旁注（\tn{marginparwidth}、\tn{marginparsep}）计入 \gpart{body} 部分中。
\end{keyval}

\begin{keyval}[path=layout]{includeall}
  \begin{syntax}
    includeall = <&\TTF> & false 
  \end{syntax}
设置 \opt{includeheadfoot} 及 \opt{includemarginpar}。
\end{keyval}

\begin{keyval}[path=layout]{ignorehead,ignorefoot,
  ignoreheadfoot,ignorehf}
  \begin{syntax}
    ignorehead = <&\TTF> & false 
    ignorefoot = <&\TTF> & false
    ignoreheadfoot|ignorehf = <&\TTF>
  \end{syntax}
在计算垂直方向的尺寸时，不考虑页眉、页脚。但不修改页眉页脚的尺寸。
\end{keyval}

\begin{keyval}[path=layout]{ignoremarginpar,ignoremp}
  \begin{syntax}
    ignoremarginpar|ignoremp = <&\TTF> & false 
  \end{syntax}
在计算水平方向的尺寸时，不考虑旁注的尺寸。但不修改旁注的尺寸。
\end{keyval}

\begin{keyval}[path=layout]{ignoreall}
  \begin{syntax}
    ignoreall = <&\TTF> & false 
  \end{syntax}
设置 \opt{ignoreheadfoot} 及 \opt{ignoremarginpar}。
\end{keyval}

\begin{keyval}[path=layout]{heightrounded}
  \begin{syntax}
    heightrounded = <&\TTF> & false
  \end{syntax}
如果设置为真，则将 \opt{textheight} 设置为不小于原 \opt{textheight} 且满足关系：
\[ n\times{}\text{\tn{baselineskip}}{}+{}\text{\tn{topskip}}\] 
的最小值。
\end{keyval}

% from geometry.dtx
\thisfloatsetup{margins=hangoutside,capposition=beside,
  capbesideposition={top,outside},floatwidth=\textwidth}
\begin{figure}[htb]
 \centering\small
 {\unitlength=.65pt
 \begin{picture}(460,525)(0,0)
 \put( 20,310){\framebox(120,170){}}
 \put( 20,507){\makebox(120,0)[bl]%
 {\textbf{(a)}~\opt{includeheadfoot}}}
 \put( 20,460){\line(1,0){120}}\put( 20,450){\line(1,0){120}}
 \put( 20,330){\line(1,0){120}}
 \put( 20,485){\makebox(120,0)[br]{\gpart{total body}}}
 \put( 20,335){\makebox(120,0)[bc]{\texttt{textwidth}}}
 \put(150,470){\makebox(0,0)[l]{\texttt{headheight}}}
 \put(150,450){\makebox(0,0)[l]{\texttt{headsep}}}
 \put(150,390){\makebox(0,0)[l]{\texttt{textheight}}}
 \put(150,320){\makebox(0,0)[l]{\texttt{footskip}}}
 \put( 10,460){\makebox(120,20)[bc]{\gpart{head}}}
 \put( 10,320){\makebox(120,140)[c]{\gpart{body}}}
 \put( 10,310){\makebox(120,10)[c]{\gpart{foot}}}
 \put(250,310){\framebox(120,170){}}
 \put(250,507){\makebox(120,0)[bl]%
 {\textbf{(b)}~\opt{includeall}}}
 \put(250,460){\line(1,0){95}}\put(250,450){\line(1,0){95}}
 \put(250,330){\line(1,0){95}}\put(345,330){\line(0,1){120}}
 \put(350,330){\line(0,1){120}}\put(350,450){\line(1,0){20}}
 \put(350,330){\line(1,0){20}}
 \put(250,485){\makebox(120,0)[br]{\gpart{total body}}}
 \put(250,460){\makebox(95,20)[bc]{\gpart{head}}}
 \put(250,320){\makebox(95,140)[c]{\gpart{body}}}
 \put(385,390){\makebox(95,0)[cl]%
 {\gpart{\shortstack[l]{marginal\\note}}}}
 \put(250,310){\makebox(95,10)[c]{\gpart{foot}}}
 \put(250,335){\makebox(95,0)[bc]{\texttt{textwidth}}}
 \multiput(360, 390)(4,0){6}{\line(1,0){2}}
 \multiput(348,333)(0,-4){12}{\line(0,1){2}}
 \multiput(360,333)(0,-4){8}{\line(0,1){2}}
 \put(355,292){\makebox(0,0)[bl]{\texttt{marginparwidth}}}
 \put(345,275){\makebox(0,0)[bl]{\texttt{marginparsep}}}
 \put( 20, 40){\framebox(120,170){}}
 \put( 20,237){\makebox(120,0)[bl]%
 {\textbf{(c)}~\opt{includefoot}}}
 \put( 20, 60){\line(1,0){120}}
 \put( 20,215){\makebox(120,0)[br]{\gpart{total body}}}
 \put(150,130){\makebox(0,0)[l]{\texttt{textheight}}}
 \put(150, 50){\makebox(0,0)[l]{\texttt{footskip}}}
 \put( 20, 50){\makebox(120,160)[c]{\gpart{body}}}
 \put( 20, 40){\makebox(120,10)[c]{\gpart{foot}}}
 \put( 20, 65){\makebox(120,10)[c]{\texttt{textwidth}}}
 \put(250, 40){\framebox(120,170){}}
 \put(250,237){\makebox(120,0)[bl]%
 {\textbf{(d)}~\opt{includefoot}, \opt{includemp}}}
 \put(250, 60){\line(1,0){95}}\put(350, 60){\line(1,0){20}}
 \put(250,215){\makebox(120,0)[br]{\gpart{total body}}}
 \put(250, 50){\makebox(95,160)[c]{\gpart{body}}}
 \put(385,130){\makebox(95,0)[cl]%
 {\gpart{\shortstack[l]{marginal\\note}}}}
 \put(250, 40){\makebox(95,10)[c]{\gpart{foot}}}
 \put(250, 65){\makebox(95,0)[bc]{\texttt{textwidth}}}
 \put(345, 60){\line(0,1){150}}\put(350, 60){\line(0,1){150}}
 \multiput(360, 130)(4,0){6}{\line(1,0){2}}
 \multiput(348, 63)(0,-4){12}{\line(0,1){2}}
 \multiput(360, 63)(0,-4){8}{\line(0,1){2}}
 \put(355,22){\makebox(0,0)[bl]{\texttt{marginparwidth}}}
 \put(345, 5){\makebox(0,0)[bl]{\texttt{marginparsep}}}
 \end{picture}}
 \captionsetup{labelsep=newline}
 \caption[不同模式下的 total part]{\small
 \IfLabelOdd{fig:geometry-modes}{\raggedright}{\raggedleft}%
  不同模式下的 \gpart{total body}。
  (a) \opt{includeheadfoot}，(b) \opt{includeall}，(c) \opt{includefoot}
  及 (d) \opt{includefoot}，\opt{includemp}。
  如果 \opt{reversemarginpar} 设置为真，则交换 \gpart{marginal note} 与 \gpart{body}
  的位置。如果设置了 \opt{twoside}，则依据奇偶页交换 \gpart{marginal note}。}
 \label{fig:geometry-modes}
\end{figure}

\begin{keyval}[path=layout]{hdivide,vdivide,divide}
  \begin{syntax}
    hdivide = \{\meta{left margin},\meta{width},\meta{right margin}\}
    vdivide = \{\meta{top margin},\meta{height},\meta{bottom margin}\}
    divide  = \{\meta{length_1},\meta{length_2},\meta{length_3}\}
  \end{syntax}
设置两个值，将另一个留空或 \verb|*|。
\end{keyval}

\begin{function}{\bodylmargin,\bodytmargin,\bodyrmargin,\bodybmargin}
\gpart{total body} 的边界距离 \gpart{layout} 边界的左、上、右、下侧的长度。
在 \TikZ 中使用时可能需要用 \tn{the} 获取它们的值，
使用 \cs{dimeval}、\tn{dimexpr} 等命令以及 \pkg{calc} 宏包的功能时则不需要。

若是设置了 \opt{twoside}，它们\emph{不会}自动切换左侧和右侧的值。
\end{function}

\subsection{边距}

\nofuncskip
\begin{keyval}[path=layout]{leftmargin,left,lmargin,inner,
  rightmargin,right,rmargin,outer,hmargin,horizontalmargin}
  \begin{syntax}
    lmargin|leftmargin |left |inner = \marg{内侧边距}
    rmargin|rightmargin|right|outer = \marg{外侧边距}
    hmargin|horizontalmargin  = \{\meta{inner},\meta{outer}\} 或 \marg{水平边距}
  \end{syntax}
设置内外侧边距。注意，不论是否使用 \opt{twoside}，它们的含义都是相同的。
\end{keyval}

\begin{keyval}[path=layout]{topmargin,top,tmargin,bottommargin,bottom,bmargin,verticalmargin}
  \begin{syntax}
    tmargin|topmargin   |top    = \marg{顶部边距}
    bmargin|bottommargin|bottom = \marg{底部边距}
    vmargin|verticalmargin      = \{\meta{top},\meta{bottom}\} 或 \marg{垂直边距}
  \end{syntax}
设置上下边距。
\end{keyval}

\begin{keyval}[path=layout]{horizontalmarginratio,hmarginratio,
  verticalmarginratio,vmarginratio,marginratio}
  \begin{syntax}
    hmarginratio|horizontalmarginratio = \marg{inner ration}:\marg{outer ratio}
    vmarginratio|verticalmarginratio   = \marg{top ratio}:\marg{bottom ratio} & 2:3
    marginratio = \{\meta{hmargin ratio},\meta{vmargin ratio}\} 或 \marg{margin ratio}
  \end{syntax}
设置内外边距、上下边距的比率。

使用 \opt{oneside} 时 \opt{hmarginratio} 初始为 \texttt{1:1}，
使用 \opt{twoside} 时 \opt{hmarginratio} 初始为 \texttt{2:3}。
\end{keyval}

\begin{keyval}[path=layout]{hcentering,vcentering,centering}
  \begin{syntax}
    hcentering = <&\TTF> 
    vcentering = <&\TTF> & false 
    centering  = <&\TTF>
  \end{syntax}
设置 \opt{hmarginratio}、\opt{vmarginratio} 为 \texttt{1:1}。
\end{keyval}

\begin{keyval}[path=layout]{twoside,asymmetric,reversemarginpar,reversemp}
  \begin{syntax}
    twoside &&
    asymmetric &&
    reversemarginpar|reversemp = <&\TTF> & false 
  \end{syntax}
设置左右边距根据奇偶页进行切换。\opt{asymmetric} 并不实际切换，而是修改长度，见 \pkgdoc{geometry}。
\end{keyval}

\begin{keyval}[path=layout]{bindingoffset}
  \begin{syntax}
    bindingoffset = \marg{长度}
  \end{syntax}
从内侧移除 \meta{长度}。
\end{keyval}

% from geometry.dtx
\thisfloatsetup{margins=hangoutside,capposition=beside,
  capbesideposition={top,outside},floatwidth=\textwidth}
\begin{figure}[htb]
 \centering\small
 {\unitlength=.65pt
 \begin{picture}(500,270)(0,0)
 \put(20,0){\framebox(170,230){}}
 \put(20,255){\makebox(80,20)[l]{\textbf{a)}~every page for oneside or}}
 \put(20,240){\makebox(80,20)[l]{\hspace{3ex}odd pages for twoside}}
 \put(110,225){\makebox(80,20)[r]{\gpart{paper}}}
 \put(55,37){\framebox(110,170)[tc]{\gpart{total body}}}
 \multiput(38,0)(0,7){33}{\line(0,1){4}}
 \put(38,100){\vector(1,0){17}}\put(55,100){\vector(-1,0){17}}
 \put(60,95){\makebox(80,10)[l]{\texttt{left}}}
 \put(60,80){\makebox(80,10)[l]{(\texttt{inner})}}
 \put(165,100){\vector(1,0){25}}\put(190,100){\vector(-1,0){25}}
 \put(195,95){\makebox(80,10)[l]{\texttt{right}}}
 \put(195,80){\makebox(80,10)[l]{(\texttt{outer})}}
 \put(20,16){\vector(1,0){18}}
 \put(45,10){\makebox(80,10)[bl]{\texttt{bindingoffset}}}
 \put(280,255){\makebox(80,20)[l]{\textbf{b)}~even (back) pages for twoside}}
 \put(280,0){\framebox(170,230){}}
 \put(370,225){\makebox(80,20)[r]{\gpart{paper}}}
 \put(305,37){\framebox(110,170)[tc]{\gpart{total body}}}
 \multiput(432,0)(0,7){33}{\line(0,1){4}}
 \put(280,100){\vector(1,0){25}}\put(305,100){\vector(-1,0){25}}
 \put(310,95){\makebox(80,10)[l]{\texttt{outer}}}
 \put(310,80){\makebox(80,10)[l]{(\texttt{right})}}
 \put(415,100){\vector(1,0){17}}\put(432,100){\vector(-1,0){17}}
 \put(373,95){\makebox(80,10)[l]{\texttt{inner}}}
 \put(373,80){\makebox(80,10)[l]{(\texttt{left})}}
 \put(450,16){\vector(-1,0){18}}
 \put(330,10){\makebox(80,10)[bl]{\texttt{bindingoffset}}}
 \end{picture}}
 \captionsetup{labelsep=newline}
 \caption[\texttt{bindingoffset} 选项]{%
  \IfLabelOdd{fig:bindingoffset}{\raggedright}{\raggedleft}\small
  The option \opt{bindingoffset} adds the specified length to the inner margin.
  Note that \opt{twoside} option swaps the horizontal margins and the
  marginal notes together with \opt{bindingoffset} on even pages (see
  \textbf{b}), but \opt{asymmetric} option suppresses the swap of the
  margins and marginal notes (but \opt{bindingoffset} is still swapped).}
 \label{fig:bindingoffset}
\end{figure}


\subsection{原有的变量}

本小节描述几个 \LaTeXe 原有的长度变量。

\begin{keyval}[path=layout]{footnotesep}
  \begin{syntax}
    footnotesep = \marg{弹性长度}
  \end{syntax}
设置 \tn{skip}\tn{footins}，即正文底部与脚注顶部的距离。
\end{keyval}

\begin{keyval}[path=layout]{marginparwidth,marginpar,marginparsep,nomarginpar,nomp}
  \begin{syntax}
    marginparwidth|marginpar = \marg{长度}
    marginparsep = \marg{长度}
    nomarginpar|nomp &&
  \end{syntax}
设置旁注宽度及旁注与正文的距离。\opt{nomarginpar} 将它们设置为 \texttt{0pt}。
\end{keyval}

\begin{keyval}[path=layout]{columnsep,twocolumn,onecolumn}
  \begin{syntax}
    columnsep = \marg{长度}
    twocolumn &&
    onecolumn &&
  \end{syntax}
设置 \tn{columnsep}，即两栏之间的距离。
\end{keyval}

\begin{keyval}[path=layout]{hoffset,voffset,offset}
  \begin{syntax}
    hoffset = \marg{长度}
    voffset = \marg{长度}
    offset  = \{\meta{hoffset},\meta{voffset}\} 或 \marg{长度}
  \end{syntax}
设置 \tn{hoffset}、\tn{voffset}。
\end{keyval}


\subsection{页眉页脚}\label{sec:geometry-headfoot}

\nofuncskip 
\begin{keyval}[path=layout]{headheight,head,headsep}
  \begin{syntax}
    head|headheight = \marg{长度}
    headsep = \marg{长度}
    nohead
  \end{syntax}
\opt{headheight} 设置 \tn{headheight}，即页眉的高度。

\opt{headsep} 设置 \tn{headsep}，即页眉与正文之间的距离。

\opt{nohead} 将它们设置为 \texttt{0pt}。
\end{keyval}

\begin{keyval}[path=layout]{footskip,foot,nofoot}
  \begin{syntax}
    footskip|foot = \marg{弹性长度}
  \end{syntax}
设置 \tn{footskip}，即正文最后一行的基线与页脚基线的距离。

\opt{nofoot} 将它设置为 \texttt{0pt}。
\end{keyval}

\begin{keyval}[path=layout]{noheadfoot,nohf}
  \begin{syntax}
    noheadfoot|nohf &&
  \end{syntax}
同时设置 \opt{nohead} 和 \opt{nofoot}
\end{keyval}

\begin{keyval}[path=layout]{headoffset,footoffset,hfoffset}
  \begin{syntax}
    headoffset = \marg{长度} & 0pt 
    headoffset = \oarg{位置} \marg{长度}
  \end{syntax}
设置页眉页脚偏移量。

\meta{位置} 为 \texttt{O}、\texttt{E} 与 \texttt{L}、\texttt{C}、\texttt{R} 的组合。
这五个值分别代表奇偶、左中右。不区分大小写。

若 \meta{长度} 为正值，则相较于 \opt{textwidth} 伸长 \meta{长度}。否则，缩短 \meta{长度}。

此选项在直排文档中可能无效。
\end{keyval}

\subsection{杂项}

本小节列出其它几个选项。未列出的选项请参考 \pkgdoc{geometry}。

% \nofuncskip
\begin{keyval}[path=layout]{showframe,showcrop,showmarking,marking}
  \begin{syntax}
    showframe = <&\TTF> & false 
    showcrop  = <&\TTF> & false 
    showmarking|marking = <&\TTF> & false 
  \end{syntax}
\opt{showframe} 显示各部分的外框。\opt{showcrop} 在 \gpart{layout} 四角显示裁剪标记。
\opt{marking} 在各部分着以彩色背景。
\end{keyval}

\begin{keyval}[path=layout]{preset,name}
  \begin{syntax}
    preset|name = \marg{preset name}
  \end{syntax}
使用预设值 \meta{preset name}。
\end{keyval}

% \begin{keyval}[path=layout]{verbose}
%   \begin{syntax}
%     verbose = <&\TTF> & false 
%   \end{syntax}
% 将各变量的结果显示在终端中。设置为 \opt{false} 时，仍将其写入日志文件中。

% 仅可用于导言区。
% \end{keyval}

% \begin{keyval}[path=layout]{pass}
%   \begin{syntax}
%     pass &&
%   \end{syntax}
% 仅可用于导言区。
% \end{keyval}


\subsection{设置页眉页脚}\label{sec:pagestyle}

本小节设置页眉页脚内容的接口。关于设置页眉页脚位置和高度的接口，见\cref{sec:geometry-headfoot}。

本节所述内容可能在直排文档中不可用。

本节所述的功能主要通过 \pkg{fancyhdr} 实现。

\begin{function}{\usepagestyle, \usethispagestyle}
  \begin{syntax}
    \verb|\usepagestyle| \marg{pagestyle}
  \end{syntax}
\cs{usepagestyle} 页眉页脚的样式 \meta{pagestyle}。\cs{usethispagestyle} 设置本页的样式。

有预定义的样式 \texttt{totalempty} 以及 \texttt{totalempty*}，它将页眉页脚设置为空，并将页眉页脚横线的厚度设为 \texttt{0pt}，后者还会把页眉页脚的额外伸长设置为 \texttt{0pt}。
\end{function}

\begin{function}[EXP]{\@currpagestyle, \@specialstyle}
只读。展开为当前使用的样式名，或 \tn{usethispagestyle} 设置的样式名。
\end{function}

\begin{function}{\setpagestyle,\copypagestyle}
  \begin{syntax}
    \verb|\setpagestyle|   \marg{pagestyle} \marg{code}
    \verb|\setpagestyle|   \marg{pagestyle_1} \oarg{pagestyle_2} \marg{code}
    \verb|\setpagestyle| * \marg{pagestyle_1} \oarg{pagestyle_2} \marg{code}
    \verb|\setpagestyle| . \marg{pagestyle_1} \oarg{pagestyle_2} \marg{code}
    \verb|\setpagestyle| + \marg{pagestyle_1} \oarg{pagestyle_2} \marg{code}
    \verb|\setpagestyle| ! \marg{pagestyle_1} \oarg{pagestyle_2} \marg{code}
    \V\copypagestyle  \marg{pagestyle_1} \marg{pagestyle_2}
  \end{syntax}
设置样式 \marg{pagestyle}，或基于样式 \meta{pagestyle_2} 设置 \meta{pagestyle_1}。
如果 \meta{pagestyle_2} 发生改变，那么 \meta{pagestyle_1} 也可能随之改变。

不带其它符号的，仅设置而不使用。
带 \verb|*| 的，还会立刻使用该样式，但不会修改 \cs{usethispagestyle} 所设置的样式。
带 \verb|.| 的，修改 \cs{usethispagestyle} 所设置的样式。
带 \verb|+| 的，相当于同时使用 \cs{usepagestyle} 和 \cs{usethispagestyle}。
带 \verb|!| 的，等同于 \cs{fancypagestyle}\texttt{*}。

\cs{copypagestyle} 把样式 \meta{pagestyle_2} 复制给 \meta{pagestyle_1}。
\end{function}

\begin{function}{\sethead,\setfoot,\setheadfoot,
  \setlefthead,\setcenterhead,\setrighthead,
  \setleftfoot,\setcenterfoot,\setrightfoot}
  \begin{syntax}
    \verb|\sethead|         \,\marg{code}
    \verb|\sethead| \oarg{位置} \marg{code}
    \verb|\setcenterhead|           \,\marg{奇偶页}
    \verb|\setcenterhead| \oarg{偶数页} \marg{奇数页}
  \end{syntax}
设置页眉页脚的内容。

\meta{位置} 为 \texttt{O}、\texttt{E}， \texttt{L}、\texttt{C}、\texttt{R}，
\texttt{H}、\texttt{F} 此三类的组合。
这七个个值分别代表奇偶、左中右、页眉页脚。不区分大小写。

如某一类未给出，则视为该类的全部值都给出。但 \cs{sethead}、\cs{setfoot} 分别为 \texttt{H}、\texttt{F}。

例如，在 \cs{sethead} 中，\verb|L| 代表 \verb|OLH,ELH|。

它们可以直接用在导言区和正文中，将修改本页及其后页面的页眉页脚。但最好用于 
\cs{setpagestyle} 命令中，统一设置页眉页脚。
\end{function}

\begin{function}{\setheadrulewidth,\setfootrulewidth}
  \begin{syntax}
    \verb|\setheadrulewidth| \marg{长度表达式}
  \end{syntax}
设置页眉、页脚横线的厚度。

（即宏 \tn{headrulewidth}、\tn{footrulewidth} 的值。）
\end{function}

\begin{function}{\setheadruleskip,\setfootruleskip}
  \begin{syntax}
    \verb|\setheadruleskip| \marg{skip expr}
  \end{syntax}
设置页眉、页脚横线与页眉、页脚文字的距离。

（即宏 \tn{headruleskip}、\tn{footruleskip} 的值。）
\end{function}

\begin{function}{\setheadrule,\setfootrule}
  \begin{syntax}
    \verb|\setheadrule| \marg{code}
  \end{syntax}
设置页眉、页脚的横线。页眉的横线的总高度最好为 0。
\end{function}

\begin{function}{\setheadinit,\setfootinit,\setheadfootinit}
  \begin{syntax}
    \verb|\setheadinit| \marg{code}
  \end{syntax}
在输出页眉页脚前要执行的 \meta{code}。
\end{function}

\begin{function}{\fancycenter}
  \begin{syntax}
    \verb|\fancycenter| \marg{left} \marg{center} \marg{right}
    \verb|\fancycenter| \oarg{distance} \oarg{stretch} \marg{left} \marg{center} \marg{right}
  \end{syntax}
它创建一个盒子，使得 \meta{center} 位于当前行（或盒子）的中心。可以用于正文中。

\meta{center} 的中心与 \meta{left}、\meta{right} 的中心的距离可能并不一致。
\end{function}

\begin{xample}
\fancycenter{L}{CCCC}{RRRRRRRRRRRRRRRR}
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\iftopfloat,\ifbotfloat,\iffloatpage,\iffootnote}
  \begin{syntax}
    \verb|\iftopfloat| \marg{true} \marg{false}
  \end{syntax}
检测当前页是否顶部、底部有浮动体，或当前页是否是浮动体页，或当前页是否有脚注。
\end{function}


\section{盒子和填充，\cusmodule{box}模块}

\cusmodule{box} 用于提供盒子构造、内容填充等内容。

\begin{function}{\spreadtext}
  \begin{syntax}
    \V\spreadtext   \marg{width} \marg{material}
    \V\spreadtext * \marg{width} \marg{material}
  \end{syntax}
在宽 \meta{width} 的盒子中把 \meta{material} 分散排列。

带星号的命令还会在 \meta{material} 两端插入等量的间距。

此命令在 \CusLaTeX 支持的引擎中都有效。
在 \LuaLaTeX 引擎中，需加载 \pkg{ctex} 或 \pkg{luatexja} 宏包；
在 \XeLaTeX 引擎中，需加载 \pkg{ctex} 或 \pkg{xeCJK} 宏包；
在 \upLaTeX 引擎中，无需加载其它宏包。
\end{function}

\begin{xample}
\fbox{\spreadtext {5cm}{你好 Hello World 世界}}

\fbox{\spreadtext*{5cm}{你好 Hello World 世界}}
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\useinfiniteskip}
  \begin{syntax}
    \V\useinfiniteskip \marg{fil dimen}
  \end{syntax}
把单词或 CJK 文字之间的距离设置为无限可伸长的间距，它比 \cs{spreadtext} 更为底层。

\meta{fil dimen} 为 \veta{factor}\texttt{fil} 或 \veta{factor}\texttt{fill}
或 \veta{factor}\texttt{filll}。\veta{factor} 为一个实数。
对于 \texttt{fil}、\texttt{fill}、\texttt{filll}，它们的阶数依次增加，
在同一个盒子中，高阶优先生效，低阶的无效。若阶数相同，则间距的长度由 \veta{factor} 控制，
在同一个盒子中相同的 \veta{factor} 保证间距相等。

它应当在一个组中使用。
\end{function}

\begin{xample}
\parbox{8\ccwd}{\useinfiniteskip{1fil}%
  \rightskip0pt plus 1fil \parfillskip-\rightskip
  测试一下哦哈哈哈 ac b 哦哈}%
\quad
\parbox{5\ccwd}{\useinfiniteskip{1fill}%
  测试一下\\测试一下\\测试一下测试一下}
\stopxamplecode
\xampleprint
\end{xample}

\begin{xample}
\fbox{\makebox[10cm][s]{%
  {\useinfiniteskip{1fil}梅川库子}%
  \hspace{0pt plus 3fil}%
  {\useinfiniteskip{1.5fil}先生}}%
}
\stopxamplecode
\xampleprint
\end{xample}

\subsection{Framed}

\cusmodule{box} 模块定义了一个简易的可跨页的盒子环境 \env{Framed}，相较于 
\pkg{tcolorbox} 宏包提供的环境来说，使用此环境的速度更快。它也可配合 \pkg{tcolorbox}
宏包使用。

\begin{function}[type=environment]{Framed}
  \begin{syntax}
    \verb|\begin{Framed}| \oarg{frame key-val}
    ~~... 
    \verb|\end{Framed}|
  \end{syntax}
创建一个可跨页的盒子。若在另一个盒子内则不可跨页。
\end{function}

\begin{keyval}[path=frame]{outer-sep}
  \begin{syntax}
    outer-sep = \marg{skip expr} & 8pt plus 8pt minus 6pt 
  \end{syntax}
设置盒子与上下文的间距。
\end{keyval}

\begin{keyval}[path=frame]{sep}
  \begin{syntax}
    sep = \marg{长度表达式} & \V{3\fboxsep}
  \end{syntax}
设置变量 \tn{cusframesep}，即盒子外框与内容的间距。
\end{keyval}

\begin{keyval}[path=frame]{rule-width}
  \begin{syntax}
    rule-width = \marg{长度表达式} & \V\fboxrule
  \end{syntax}
设置变量 \tn{cusframerule}，即盒子外框的厚度。
\end{keyval}

\begin{keyval}[path=frame]{frame,frame*,
  first,first*,middle,middle*,last,last*,
  whole,whole*}
  \begin{syntax}
    frame  = \marg{code}
    frame* = \marg{code width 1 parameter}
  \end{syntax}
\opt{frame} 设置盒子外框。

\opt{first}，\opt{middle}，\opt{last} 设置分页盒子的前、中、后三部分的外框。

\opt{whole} 设置未分页盒子的外框。

\meta{code} 其后可接一个参数，这个参数为分页后的盒子。
\meta{code with 1 parameter} 显式给出变量 \verb|#1|。
\end{keyval}

\begin{keyval}[path=frame]{init}
  \begin{syntax}
    init = \marg{code}
  \end{syntax}
盒子中执行的初始化代码。
\end{keyval}

\begin{keyval}[path=frame]{width,ratio}
  \begin{syntax}
    width = \marg{长度表达式} & \V\textwidth
    ratio = \marg{数值表达式} & 1
  \end{syntax}
\opt{ratio} 设置盒子内容（含边框）占 \opt{width} 的比率。
\end{keyval}

\begin{keyval}[path=frame]{align,
  left,center,right,inner,outer}
  % absinner,absouter,inner*,outer*,
  \begin{syntax}
    align = <&left|center|right|inner|outer> & center 
  \end{syntax}
设置水平对齐方式。当 $ 0 <{} \text{\meta{ratio}} {}< 1 $ 时才有效。
\end{keyval}

\begin{xample}
\begin{Framed}[ratio=.8,center,
  rule-width=2pt,
  frame={\setlength{\fboxsep}{\cusframesep}%
          \setlength{\fboxrule}{\cusframerule}%
          \fcolorbox{purple}{cyan!50}}]
\zhlipsum[9][name=zhufu]
\end{Framed}
\stopxamplecode
\xampleprint 
\vskip 1pt 
\end{xample}

\begin{keyval}[path=frame]{ignore-warnings}
  \begin{syntax}
    ignore-warnings &&
  \end{syntax}
忽略部分警告。
\end{keyval}


\subsection{Filler}

“filler”是用以填充空间的那部分内容。如 \LaTeXe 的 \tn{hrulefill} 是用水平直线填充，
\tn{dotfill} 是用句点填充，\tn{hspace} 是用空白填充。

\cusmodule{box} 提供了几个创建 filler 的命令。

\begin{function}{\dashfiller}
  \begin{syntax}
    \verb|\dashfiller|
    \verb|\dashfiller| \marg{filler width}
    \verb|\dashfiller| \oarg{raise}\oarg{sep width}\oarg{rule width}
    \verb|\dashfiller| \oarg{raise} \marg{filler width}\oarg{sep width}\oarg{rule width}
  \end{syntax}
使用虚线填充，虚线宽和虚线间的距离近似为 \meta{sep width}，使得虚线充满 \meta{filler width}。

\begin{itemize}[nosep]
  \item \meta{filler width} 为总宽度，默认值为 \tn{linewidth}。
  \item \meta{raise} 为虚线升高的高度，默认为 \texttt{0pt}。
  \item \meta{sep width} 为虚线宽和虚线间的距离，默认为 \texttt{1ex}。
  \item \meta{rule width} 为虚线的厚度，默认为 \texttt{0.4pt}。
\end{itemize}
\end{function}

\begin{xample}
\noindent\llap{|}\dashfiller \par % 总长为 \linewidth 
\noindent\llap{|}\dashfiller [.5ex] \par % 升高 .5ex
% 升高 .5ex，宽 3pt，注意第二个可选参数前不能有空格
\noindent\llap{|}\dashfiller [.5ex][3pt] \par 
\stopxamplecode
\xampleprint 
\end{xample}

\begin{function}{\filler}
  \begin{syntax}
    \verb|\filler| \oarg{filler key-val}
  \end{syntax}
使用给定内容填充。
\end{function}

\begin{keyval}[path=filler]{size,size*}
  \begin{syntax}
    size  = \marg{skip expr}
    size* = \marg{长度}
  \end{syntax}
设置填充的长度。\opt{size*} 填充的长度是弹性的。

注意在行间数学模式中（\env{equation}、\env{align} 等环境）弹性的那部分长度无效。
\end{keyval}

\begin{keyval}[path=filler]{space,hspace,hspace*,vspace,vspace*,not-space}
  \begin{syntax}
    space   = <&*|\meta{code}>
    hspace  = <&*|\meta{skip expr}>
    hspace* = <&*|\meta{skip expr}>
    vspace  = <&*|\meta{skip expr}>
    vspace* = <&*|\meta{skip expr}>
    not-space &&
  \end{syntax}
使用空白填充。使用它后，其它选项无效。

\meta{code} 是填充的内容，如 \verb|\hspace{1cm}|，\verb|\vspace*{1cm}|。

\opt{hspace} 相当于设置 \verb|space=\hspace|\marg{skip expr}。

\opt{hspace*} 相当于设置 \verb|space=\breakablehspace|\marg{skip expr}。

\opt{vspace} 相当于设置 \verb|space=\vspace|\marg{skip expr}。

\opt{vspace*} 相当于设置 \verb|space=\breakablevspace|\marg{skip expr}。

当值为 \texttt{*} 时，使用 \keyref[filler]{size} 或 \keyref[filler]{size*} 设置的值。
若设置 \verb|space=*|，则会根据是否处于垂直模式而使用 \tn{vspace} 或 \tn{hspace}。

由于用 space 填充的优先级最高，若设置使用 space 填充后，要使用其它类型来填充，需使用
\opt{not-space} 或将 \opt{space} 设置为空。若后续仍设置了 \opt{space}，则仍会使用 space 填充。

\cs{breakablehspace} 和 \cs{breakablevspace} 是可断开的水平或垂直空白。实际给出的空白
与要求的有一定的误差。
\end{keyval}

\begin{xample}
左 \fbox{\strut \filler [hspace=5cm]} 右间隔约 5cm。

左 \filler[space] 右拉开。

左 \filler[space] 中 \filler[space] 右拉开。
\stopxamplecode
\xampleprint
\end{xample}

\begin{keyval}[path=filler]{color}
  \begin{syntax}
    color = <{color expr}>
  \end{syntax}
设置颜色 \key[module=color name]{cusfiller}，即填充的颜色。
\end{keyval}

\begin{keyval}[path=filler]{content,box,box*,clear-box}
  \begin{syntax}
    content = <{content}>
    box     = <{content}>
    box*    = <{长度表达式}> <{content}>
    clear-content &&
    clear-box &&
    not-content &&
  \end{syntax}
使用长 \meta{长度表达式} 的 \meta{content} 填充。

\opt{content} 和 \opt{box} 选项基本一致，只是 \opt{content} 会自动设置颜色，而
\opt{box} 则需使用 \tn{color} 或 \cs{color_select:n} 来设置颜色。

使用 \opt{content} 将使用 \meta{content} 的自然宽度，而 \opt{box*} 则使用指定的宽度。

当设置了 \opt{box} 或 \opt{box*} 后，\opt{content} 无效，除非使用 \opt{clear-box} 清除 box。
\end{keyval}

\begin{keyval}[path=filler]{dash,sep,rule,raise,full,is-dash}
  \begin{syntax}
    dash|sep = \marg{dash length} & 0pt 
    rule     = \marg{rule width} & 0.4pt 
    raise    = \marg{raise height} & 0pt 
    full     = <&\TTF> & false 
    is-dash &&
  \end{syntax}
使用虚线填充。

虚线宽和虚线间距为 \meta{dash length}，厚度为 \meta{rule width}，升高 \meta{raise height}。

若 \meta{dash length} 为 \texttt{0pt}，则使用实线填充。

如果设置 \opt{full} 为真，则相当于 \cs{dashfiller}。
\end{keyval}

\begin{keyval}[path=filler]{solid,dashed,dotted,cdotted}
  \begin{syntax}
    solid &&
    dashed  = \marg{长度}
    dotted  = \marg{间距}
    cdotted = \marg{间距}
  \end{syntax}
使用实线、虚线、句点或 \tn{cdot} 填充。
\end{keyval}

\begin{xample}
\def\BL{\noindent\llap{|}}%
\BL \filler[color=red, solid, rule=2pt] \par
\BL \filler[color=red, dashed, rule=0.5ex] \par 
\BL \filler[color=red, dashed, rule=0.5ex, full] \par 
\BL \filler[color=red, dotted] \par 
\BL \filler[color=red, cdotted] \par 
\BL \filler[color=red, cdotted=1cm, align] \par % 每个点都是对齐的
\BL \filler[color=red, cdotted=1cm, center] \par % 每个点的间距都是 1cm
\BL \filler[color=red, cdotted=1cm, spread] % 每个点的间距都相等，可能超过1cm
\stopxamplecode
\xampleprint
\end{xample}

\begin{keyval}[path=filler]{type,align,center,spread}
  \begin{syntax}
    type  = <&align|center|spread> & align 
    align &&
    center &&
    spread &&
  \end{syntax}
构造填充的方式。

\begin{itemize}[nosep]
  \item \opt{align}：每个同种 filler 都是无限长的对齐的填充中的一部分，因此，它们处处都是对齐的；
  \item \opt{center}：把用以填充的盒子紧挨着排列，两头留下相等的空白；
  \item \opt{spread}：把多余的空白均匀地分布在盒子中间及两侧。
\end{itemize}
\end{keyval}

\begin{texnote}
实际这三种方式分别对应 \tn{leaders}、\tn{cleaders}、\tn{xleaders}。
\end{texnote}

\begin{function}{\atleastfiller}
  \begin{syntax}
    \verb|\atleastfiller| \marg{dim expr}
    \verb|\atleastfiller| \oarg{filler key-val} \marg{dim expr}
  \end{syntax}
填充的长度至少为 \meta{dim epxr}，太短的将自动断行。
\end{function}

\begin{xample}
我能吞下玻璃而不伤身体，I can eat glass, it doesn't hurt me.%
\atleastfiller[cdotted=1em]{5cm}断行。

我能吞下玻璃而不伤身体。\atleastfiller[cdotted=1em]{5cm}不断。
\stopxamplecode
\xampleprint 
\end{xample}

\begin{function}{\breakablefiller}
  \begin{syntax}
    \verb|\breakablefiller|
    \verb|\breakablefiller| \oarg{filler key-val}
  \end{syntax}
可自动断行的 filler。默认使用空格填充。
\end{function}

\begin{xample}
\framebox[3cm]{可断} \breakablefiller[cdotted=1em] \framebox[3cm]{模式。}

\framebox[7cm]{可断} \breakablefiller[cdotted=1em] \framebox[7cm]{模式。}
\stopxamplecode
\xampleprint
\end{xample}

下例展示了制作多行填充的例子。
\begin{xample}
\newcommand\filllines[4][]{{% filler key-val, before, lines, after
  #2\filler[#1]%
  \Replicate{#3-1}{\break \rule{0pt}{0.7\baselineskip}\filler[#1]}%
  #4\par}}

我能吞下玻璃而不伤身体，I can eat glass, it doesn't hurt me.
\filllines{\linespread{2}\selectfont}{3}{。\hspace*{1em}}

我能吞下玻璃而不伤身体，I can eat glass, it doesn't hurt me.
\filllines[color=red,dotted]{\linespread{2}\selectfont}{3}{。\hspace*{1em}}

% 整行
\filllines [raise=-.5ex]{\linespread{2}\selectfont \noindent\strut}{3}{ 整行。\hspace*{1em}}
\stopxamplecode
\xampleprint  
\end{xample}


\subsection{多栏文字}\label{sec:multicol}

\CusTeX 中排版多栏文字有两种方式，本节描述其中一种，使用 \pkg{multicol} 宏包实现。另一种方式见\cref{sec:lib-box}。

在 \pkg{multicol} 中，可以使用 
\begin{xample}
\begin{multicols}{col}
  ... 
\end{multicols}
\stopxamplecode 
\xamplecode \medskip 
\end{xample}
来排版多栏文字。本模块对其进行了简单的封装，使得可以通过键值方式来设置相关变量。

关于每个内部变量的详细用法，可以参考 \pkgdoc{multicol}。

\begin{function}{\startmulticolumns,\stopmulticolumns}
  \begin{syntax}
    \verb|\startmulticolumns| \oarg{multicolumns key-val}
    ~~<content>
    \verb|\stopmulticolumns|
  \end{syntax}
将 \meta{content} 以多栏排版。
\end{function}

\begin{keyval}[path=multicolumns]{columns,cols}
  \begin{syntax}
    columns|cols = \marg{整数表达式} & 2
  \end{syntax}
设置多栏数。也可不必写出键名，只写数字。

可用的栏数为 1--20，如果 \keyref[multicolumns]{adj} 为
\opt{true}，则可用的栏数为 1--10。
\end{keyval}

\begin{keyval}[path=multicolumns]{outer-sep}
  \begin{syntax}
    outer-sep = \marg{skip expr} & 12.0pt plus 4.0pt minus 3.0pt
  \end{syntax}
设置 \tn{multicolsep}，即多栏文字与上下文的间距。
\end{keyval}

\begin{keyval}[path=multicolumns]{column-sep,sep}
  \begin{syntax}
    column-sep|sep = \marg{length} & 10pt 
  \end{syntax}
设置 \tn{columnsep}，即多栏文字两栏的间隙。
\end{keyval}

\begin{keyval}[path=multicolumns]{first-minimal,last-minimal}
  \begin{syntax}
    first-minimal = \marg{pre length} & 50pt 
    last-minimal  = \marg{post length} & 20pt 
  \end{syntax}
如果多栏开始的那一页不足 \meta{pre length}，则多栏将在新的一页开始。

如果多栏结束的那一页不足 \meta{post length}，则多栏将在新的一页结束。

\opt{first-minimal} 设置 \tn{premulticols}，
\opt{last-minimal} 设置 \tn{postmulticols}。
\end{keyval}

\begin{keyval}[path=multicolumns]{heading}
  \begin{syntax}
    heading = \marg{content}
  \end{syntax}
设置在多栏文字之前的横跨所有栏的文字。可以使用 \tn{section} 等。它与其后的多栏文字保持在同一页。
\end{keyval}

\begin{keyval}[path=multicolumns]{rule-width,rule-color}
  \begin{syntax}
    rule-width = \marg{length} & 0pt 
    rule-color = \marg{color}
    rule-color = \oarg{color mode} \marg{color}
  \end{syntax}
设置 \tn{columnseprule}、\tn{columnseprulecolor}，即多栏间竖线的宽度和颜色。
\end{keyval}

\begin{keyval}[path=multicolumns]{flush,aligned,ragged,not-aligned}
  \begin{syntax}
    flush |aligned &&
    ragged|not-aligned &&
  \end{syntax}
控制多栏文字的尾部是否对齐。分别设使用 \tn{flushcolumns} 和 \tn{raggedcolumns}。

初始为 \opt{aligned}，将使各栏头部和尾部的基线尽量对齐。
\end{keyval}

\begin{keyval}[path=multicolumns]{balanced,not-balanced}
  \begin{syntax}
    balanced = <&\TTF> & true 
    not-balanced &&
  \end{syntax}
在末页文字的处理上，有两种方式，一种为文字尽量往上排，而将下方留空，这也是默认的方式；
另一种为文字尽量往左排（右排），而将右边（左边）留空，也就是将空白留在末尾的几栏上。
前者为 \opt{balanced}，后者为 \opt{not-balanced}。
\end{keyval}

\begin{keyval}[path=multicolumns]{columns*,cols*}
  \begin{syntax}
    columns*|cols* = \marg{栏数}
  \end{syntax}
它在设置栏数的同时还设置 \opt{not-balanced}。

注意 \opt{columns} 并未决定使用 \opt{balanced} 还是 \opt{not-balanced}。
\end{keyval}

\begin{keyval}[path=multicolumns]{swap-column,enable-swap-column,disable-swap-column}
  \begin{syntax}
    swap-column = <&\TTF> & false 
    enable-swap-column &&
    disable-swap-column &&
  \end{syntax}
控制是否在使用了 \opt{twoside} 文档类选项时，偶数页逆序输出各栏。

\opt{enable-swap-column} 用于启用此功能，\opt{disable-swap-column} 用于禁用此功能。
\end{keyval}

\begin{keyval}[path=multicolumns]{framed,framed-options,framed-options+}
  \begin{syntax}
    framed = <&none|fbox|...>
    framed-options = \marg{options}
  \end{syntax}
控制多栏文字整体用哪种盒子框住，多栏文字仍然可以分页。
\opt{framed-options} 为可能的选项。

默认仅提供 \opt{fbox} 这个可选值，表示用 \tn{fbox} 框住。其它库可能提供额外的值。如 
\cusmodule{tcb} 库提供 \opt{tcbox} 值（\cref{sec:multicolumns/framed=tcbox}），
\cusmodule{box} 库提供 \opt{lfbox} 值（\cref{sec:multicolumns/framed=lfbox}）。

使用此选项时，最好把 \keyref[multicolumns]{overflow} 设置为 \texttt{0pt}，否则可能无法正常分页。
\end{keyval}

\begin{keyval}[path=multicolumns]{adj,adj*,adj-inner,adj-outer,shorten,widen}
  \begin{syntax}
    adj  = <&\TTF> & false 
    adj* &&
    adj-inner = \marg{内侧长度}
    adj-outer = \marg{外侧长度}
    shorten   = \marg{长度} | \{ \meta{内侧长度}, \meta{外侧长度} \}
    widen     = \marg{长度} | \{ \meta{内侧长度}, \meta{外侧长度} \}
  \end{syntax}
多栏文字还可以通过调整边距来调整总的文字宽度。\opt{adj} 用于启用或关闭此功能。
\opt{adj*} 设置 \verb|adj=true| 及 \keyref[multicolumns]{not-balanced}。

\opt{adj-inner} 的 \meta{内侧长度} 和 \opt{adj-outer} 的 \meta{外侧长度} 
分别调整文字的内侧和外侧边距。正值表示向文字内调整
（总的文字宽度减少），负值表示向文字外调（总的文字宽度增加）。
它们自动设置 \opt{adj} 为 \opt{true}。

\opt{shorten} 的 \meta{内侧长度} 和 \meta{外侧长度} 表示内侧和外侧缩短指定值，
如果只有一项，则内侧和外侧均缩短该长度。如果任意一项为空，则相当于设置为 \texttt{0pt}。
它自动设置 \opt{adj} 为 \opt{true}。

\opt{widen} 的 \meta{内侧长度} 和 \meta{外侧长度} 表示内侧和外侧伸长指定值，
如果只有一项，则内侧和外侧均伸长该长度。如果任意一项为空，则相当于设置为 \texttt{0pt}。
它自动设置 \opt{adj} 为 \opt{true}。
\end{keyval}

\sdanger

\begin{keyval}[path=multicolumns]{wrap-box}
  \begin{syntax}
    wrap-box = <&\TTF> & false
  \end{syntax}
当增加了多栏文字的宽度时，如果这些文字还被放在一个盒子中，则可能会造成 Overfull，
设置本选项为真，则不会出现 Overfull。

这种情况常见于把这些文字放在浮动体中，因此，在浮动体中自动设置该选项为真。

一般情况无需特别设置该选项。
\end{keyval}

\begin{keyval}[path=multicolumns]{addto-baselineskip}
  \begin{syntax}
    addto-baselineskip = \marg{length}
  \end{syntax}
设置 \tn{multicolbaselineskip}。% 将 \meta{length} 加到 \tn{baselineskip} 上。
\end{keyval}

\begin{keyval}[path=multicolumns]{tolerance,pretolerance}
  \begin{syntax}
    tolerance    = \marg{int expr} & 9999
    pretolerance = \marg{int expr}
  \end{syntax}
设置 \tn{multicoltolerance}、\tn{multicolpretolerance}。
\end{keyval}

\begin{keyval}[path=multicolumns]{collectmore,minrows,unbalance,column-badness,final-column-badness}
  \begin{syntax}
    collectmore = \marg{int expr}
  \end{syntax}
设置 \texttt{collectmore}，\texttt{minrows}，\texttt{unbalance}，
\texttt{columnbadness}，\texttt{finalcolumnbadness} 计数器。
\end{keyval}

\begin{keyval}[path=multicolumns]{top-fuzz,bottom-fuzz}
  \begin{syntax}
    top-fuzz    = \marg{dim expr} & 0pt 
    bottom-fuzz = \marg{dim expr} & 2pt 
  \end{syntax}
设置 \tn{multicolovershoot}、\tn{multicolundershoot}。
\end{keyval}

\begin{keyval}[path=multicolumns]{v-fuzz,h-fuzz}
  \begin{syntax}
    v-fuzz = \marg{length}
  \end{syntax}
\opt{v-fuzz} 设置 \opt{top-fuzz} 和 \opt{bottom-fuzz}。\opt{h-fuzz} 设置 \tn{hfuzz}。
\end{keyval}

\begin{keyval}[path=multicolumns]{overflow}
  \begin{syntax}
    overflow = \marg{dim expr} & 12pt 
  \end{syntax}
设置 \tn{maxbalancingoverflow}。
\end{keyval}

\begin{keyval}[path=multicolumns]{left-to-right,LR,right-to-left,RL}
  \begin{syntax}
    left-to-right|LR &&
    right-to-left|RL &&
  \end{syntax}
使用 \tn{LRmulticolcolumns} 或 \tn{RLmulticolcolumns}。默认为 \opt{left-to-right}。
\end{keyval}

\subsection{额外增加文字的宽度}

基于多栏文字的功能，提供了 \env{extrawidth} 环境，用以为部分文字增加额外的宽度，
以及 \env{fullpagewidth} 环境，用于输出占整个纸张宽的文字。它们都有多栏环境的功能。

\begin{function}{\startextrawidth,\stopextrawidth}
  \begin{syntax}
    \V\startextrawidth \marg{inner extra width} \marg{outer extra width}
    \V\startextrawidth \oarg{multicolumns key-val} \marg{inner extra width} \marg{outer extra width}
    ~~\meta{material}
    \V\stopextrawidth
  \end{syntax}
\meta{material} 的内侧伸长 \meta{inner extra width}，外侧伸长 \meta{outer extra width}，这两个值可以为空，表示不增加额外的宽度。
\end{function}

\begin{function}{\startfullpagewidth,\stopfullpagewidth}
  \begin{syntax}
    \V\startfullpagewidth
    \V\startfullpagewidth \oarg{multicolumns} \string<\meta{inner shrink},\meta{outer shrink}\string>
    ~~<material>
    \V\stopfullpagewidth
  \end{syntax}
\meta{material} 占整个纸张的宽度。内侧留白 \meta{inner shrink}，外侧留白 \meta{outer shrink}，它们都可以为空，表示不留白。
\end{function}

\subsection{旋转的盒子}

\CusTeX 封装了 \pkg{rotating} 宏包，提供了旋转的盒子。

\begin{function}{\startrotate,\stoprotate,\Rotate}
  \begin{syntax}
    \verb|\startrotate| \oarg{rotate key-val}
    ~~\meta{content}
    \verb|\stoprotate|
    \endgraf \medskip 
    \verb|\Rotate| \oarg{rotate key-val} \marg{content}
  \end{syntax}
将 \meta{content} 旋转显示。
\end{function}

旋转的盒子有两种方式，一种为保留旋转后的盒子的大小，另一种设置旋转后的盒子大小为 0。

\begin{keyval}[path=rotate]{turn,nospaceturn,rotate,sideways}
  \begin{syntax}
    turn = \marg{number}
    rotate|nospaceturn = \marg{number}
    sideways &&
  \end{syntax}
将盒子旋转 \meta{number} 度。一般是逆时针旋转。

\opt{turn} 使用第一种方式，\opt{rotate} 使用第二种方式。\opt{sideways} 相当于 \verb|turn=90|。

\verb|\startrotate ... \stoprotate| 默认使用 \opt{turn}，\verb|\Rotate| 默认使用
\opt{rotate}。
\end{keyval}

\begin{keyval}[path=rotate]{float,float*,figure,figure*,table,table*}
  \begin{syntax}
    float  = \marg{float type}
    float* = \marg{float type}
    figure &&
    figure* &&
  \end{syntax}
将 \meta{content} 看作在浮动环境 \meta{float type} 内，并将其旋转 90 度。旋转后的内容占据一整个页面。

带 \verb|*| 类似于带 \verb|*| 的浮动环境。

也可不写出 \opt{float} 或 \opt{float*} 键名，直接写 \meta{float type} 或 
\meta{float type}\verb|*|。
\end{keyval}


\section{背景，\cusmodule{bgfg}模块}

\cusmodule{bgfg} 是对 \texttt{shipout} 钩子的简单封装。

关于“钩子”机制，\cref{sec:lthooks}对其作了简单的介绍，更详细的用法请参考：\file{lthooks-doc.pdf}。

本手册前几页的水印使用如下代码实现：
\begin{xample}
% 将盒子的内容添加到背景上
\background + [./watermark]{%
  \rotatebox{45}{\color{gray!30}\fontsize{100}{0}%
    \sffamily \CusTeX}}

% 使用如下代码即可删除此背景
\removebackground[./watermark]
\stopxamplecode
\xamplecode
\medskip 
\end{xample}

\begin{function}{\foreground,\background}
  \begin{syntax}
    \verb|\foreground|   \marg{content}
    \verb|\foreground| + \marg{content}
    \verb|\foreground| \parg{位置} \marg{content}
    \verb|\foreground| \oarg{hook label} \marg{content}
    \verb|\foreground| + \parg{位置} \oarg{hook label} \marg{content}
  \end{syntax}
将 \meta{content} 放置在前景或背景中。

\begin{itemize}[nosep]
  \item \meta{content} 为要放置的内容，该内容将完整地嵌于页面内；
  \item \meta{位置} 是 \meta{content} 要放置的位置，为两个字符，前一个为水平位置；后一个为垂直位置。水平位置包括：左（\texttt{l}）、中（\texttt{c}）、右（\texttt{r}）、内侧（\texttt{i}）、外侧（\texttt{o}）；垂直位置包括：顶部（\texttt{t}）、中部（\texttt{m}）、底部（\texttt{b}）；\gpart{layout} 的正中心 \texttt{cm} 是默认值；还可以置为空，此时 \meta{content} 中可以使用 \tn{put} 命令指定文字的位置，纸张\emph{左上角}为原点；
  \item \meta{hook label} 为 hook 的 label；此参数与 \meta{位置} 的先后位置可交换；\\ 
  \cs{foreground} 默认为 \texttt{./fg}，\cs{background} 默认为 \texttt{./bg}；\\
  关于 hook label 的作用，请参考\cref{sec:lthooks}或 \file{lthooks-doc.pdf}；
  \item 默认情况下 \meta{content} 仅添加到当前页，使用 \texttt{+} 可将 \meta{content} 添加到往后各页。不带 \texttt+ 的，\meta{hook label} 无效。
\end{itemize}
\end{function}

本例展示在页面样式的代码中为本页增加背景文字：
\needspace{3cm}
\begin{xample}
\background(lt){\textcolor{red}{\LARGE\CusTeX}}
\stopxamplecode
\xampleprint
如本页左上角所示。
\end{xample}

除了上述两个命令外，还提供了两个设置背景图片的命令。

\begin{function}{\foregroundpicture,\backgroundpicture}
  \begin{syntax}
    \verb|\foregroundpicture|     \marg{图片}
    \verb|\foregroundpicture| +   \marg{图片}
    \verb|\foregroundpicture|   * \marg{图片}
    \verb|\foregroundpicture| + * \marg{图片}
    \verb|\foregroundpicture| + * \parg{位置} \oarg{图片选项} \oarg{hook label} \marg{图片}
    \verb|\foregroundpicture| + * \oarg{图片选项} \oarg{hook label} \parg{位置} \marg{图片}
  \end{syntax}
将 \meta{图片} 添加到前景或背景中。

\texttt{+}、\meta{位置}、\meta{hook label} 的用法如前所述。其中 \meta{位置} 只能是关于位置的值。

不带 \verb|*| 的，图片被伸缩到与 \gpart{layout} 同宽高。而带星号的则仅缩放宽度，保持纵横比例不变。
\end{function}

也可直接使用 \cs{background} 放置背景图片。
\needspace{3\baselineskip}
\begin{xample}
\background(ob){\includegraphics[width=\marginparwidth]{ctanlion.pdf}}
\stopxamplecode
\xampleprint
如本页底部图片所示。
\end{xample}

% \begin{xample}
% \backgroundpicture*{ctanlion.pdf}
% \stopxamplecode
% \xampleprint
% 效果如本页所示。
% \end{xample}

\begin{function}{\removeforeground,\removebackground}
  \begin{syntax}
    \verb|\removeforeground|
    \verb|\removeforeground| \oarg{hook label}
  \end{syntax}
移除前景或背景。
\end{function}


\section{文档结构，\cusmodule{struct}模块}

\cusmodule{struct} 模块提供了创建目录和章节标题的方法。参考了 \pkg{titlesec}，
\pkg{titletoc}、\pkg{ctexheading}、\pkg{etoc} 等宏包的实现，并自动阻止加载这些宏包。

章节标题样式的设置与 \pkg{ctexheading} 宏包也即 \CTeX 文档类的接口基本一致，但扩充了几个选项，并且可以定义新的标题。

\begin{function}{\definetitle}
  \begin{syntax}
    \verb|\definetitle| \marg{title command} \marg{title key-val}
    \verb|\definetitle| \marg{title command} \oarg{title class} \marg{title key-val}
  \end{syntax}
定义新的章节标题命令 \meta{title command}，以 \meta{title class} 作为基准类。

标题的使用方式见下方预定义的几个章节标题命令。
\end{function}

标准的 {\LaTeX} book 类中，章节标题可分为三种，一种是以 \cs{part} 为代表的，\CusTeX 将其命令为 \texttt{page} 类，一种是以 \cs{chapter} 为代表的，\CusTeX 将其命名为 \texttt{top} 类，另一种则是以 \cs{section} 为代表，\CusTeX 将其命名为 \texttt{normal} 类。\footnote{实际上，这些名称基本沿用了 \pkg{titlesec} 宏包的名称。}
另外还有 \texttt{free}、\texttt{wrap} 类，详细用法见\cref{sec:struct-title-class}。

\begin{texnote}
定义章节标题时，为了使得 plain 格式和 template 格式的目录正常工作，需要分别定义
\tn[no-index]{l@\meta{title command name}} 和 \opt{templatecbl} 对象类型对应的实例。
详见\cref{ch:title-cbl}。
\end{texnote}

本模块预先定义了一些章节命令，它们与 \cls{ctexbook} 文档类的效果基本一致。

\begin{function}{\part,\chapter,\section,\subsection,\subsubsection,\paragraph,\subparagraph}
  \begin{syntax}
    \V\part   \marg{标题}
    \V\part   \oarg{cbl list entry} \marg{标题}
    \V\part   \oarg{title key-val} \marg{标题}
    \V\part   \oarg{title key-val} \oarg{cbl list entry} \marg{标题}
    \V\part - \marg{标题}
    \V\part - \oarg{cbl list entry} \marg{标题}
    \V\part - \oarg{title key-val} \marg{标题}
    \V\part - \oarg{title key-val} \oarg{cbl list entry} \marg{标题}
    \V\part * \marg{标题}
    \V\part * \oarg{title key-val} \marg{标题}
  \end{syntax}
与标准的章节标题命令相比，增加了 \meta{title key-val} 可选项，用于暂时修改样式。

\meta{标题} 为实际显示的标题，\meta{cbl list entry} 为目录、页眉等内容中的文字，它在带星号的命令中无效。

带 \texttt{-} 的命令不会增加计数器的值，也不会显示编号，其它的与不带 \texttt{-} 的命令一致。
相当于 \verb|mode=nonumber|，见 \keyref*[title/...]{mode}。
\end{function}

若要修改它们的样式，一般仅需使用下述的 \cs{setuptitle} 修改键值选项，而无需重新定义它们。

\begin{function}{\setuponetitle,\setuptitle}
  \begin{syntax}
    \V\setuponetitle  <{title level}> <{key-val}>
    \V\setuponetitleq <{title}> <{key-val}>
    \V\setuptitle \marg{title key-val}
    \V\setuptitle \oarg{title level clist} \marg{title key-val}
  \end{syntax}
设置标题的样式。\meta{title level} 为章节标题层级，如：\verb|chapter|、\verb|section|、\verb|1|。\meta{title} 为章节标题名，而非标题命令（\verb|\chapter|、\verb|\section|）。
\end{function}

\begin{function}{\setupnexttitle}
  \begin{syntax}
    \V\setupnexttitle     <{title keys}>
    \V\setupnexttitle   + <{title keys}>
    \V\setupnexttitle ?   <{title keys}>
    \V\setupnexttitle ? + <{title keys}>
  \end{syntax}
设置标题的选项 \meta{title keys}，但只作用于接下来的那个标题，这个标题完成后，会清空它。

带 \texttt{+} 的，把 \marg{title keys} 附加到之前所保存的选项之后。
带 \texttt{?} 的，仅会设置已知的选项，当选项未定义时会忽略它们。
当为某些标题定义了额外的键，但不确定接下来使用的标题是否有这些键时，它很有用。
\end{function}

以下几节描述了章节标题中可用的键值选项，它们均可以被设置，但并不一定在所有的标题类中都有效。
这里的 \texttt{...} 代表各章节标题命令的名称。


\subsection{初始化设置}

初始化设置选项可以在导言区修改任意次，但不可在正文中设置。

\begin{keyval}[path=title/...]{number-from,number-within,number-without}
  \begin{syntax}
    number-from    = \marg{计数器}
    number-within  = \marg{计数器}
    number-without = \marg{计数器}
  \end{syntax}
设置章节命令的计数器。

\opt{number-from} 设置章节命令所使用的计数器，默认为使用自己的计数器，这计数器与章节命令同名。

\opt{number-within} 设置章节计数器随该计数器的递增而清零。\opt{number-without} 取消对应的设置。
\end{keyval}

\begin{keyval}[path=title/...]{number-format,label-format}
  \begin{syntax}
    number-format = \marg{code}
    label-format  = \marg{code}
  \end{syntax}
设置输出的数字的格式（即 \tn[no-index]{the\meta{section}}）或交叉引用的格式。
\end{keyval}

\begin{keyval}[path=title/...]{level}
  \begin{syntax}
    level = \marg{整数或层级名称}
  \end{syntax}
设置章节标题的层级。将影响是否对标题进行编号。
\end{keyval}

\begin{function}[EXP]{\CurrentTitleName}
在正文中展开为此前的标题的名称。例如现在它的值为“\CurrentTitleName”。
\end{function}

\begin{function}[EXP]{\CurrentTitleLevel}
在正文中展开为此前的标题的 \veta{level}。在没有使用标题前，它展开为 $-10001$。

例如，现在它的值为 $ \CurrentTitleLevel $。
\end{function}

\begin{function}[EXP]{\thetitlecount,\PreviousTitleCount}
展开为章节标题的使用次数。

\cs{thetitlecount} 为到目前为止的使用次数，
\cs{PreviousTitleCount} 为上一次运行时章节标题的使用次数或为 0。
\end{function}

\subsection{编号}

\nofuncskip
\begin{function}{\setsecnumdepth}
  \begin{syntax}
    \verb|\setsecnumdepth| \marg{整数或层级名称}
  \end{syntax}
设置对章节标题进行编号的层次数。可以是整数或层级名称。

\CusTeX 预先定义了一些层级名称，如\cref{tab:level-name} 所示。
\end{function}

\begin{table}[htb]
\linespread{1.1}\small 
\begin{pagecenterbox}
\begin{tabular}{l>{\ttfamily}c>{\ttfamily}c>{\ttfamily}c>{\ttfamily}c>{\ttfamily}c}
  \toprule
  层级 & -1&0&1&2&3 \\ \midrule
  名称 & part&chapter&section&subsection&subsubsection \\ \specialrule{\heavyrulewidth}{\aboverulesep}{\belowrulesep}
  层级 &4&5&4&5& \\ \midrule
  名称 &paragraph&subparagraph&sub3section&sub4section& \\ \bottomrule
\end{tabular}
\end{pagecenterbox}
\captionsetup{margin={\dimeval{\paperwidth-3.4cm-\textwidth},0pt}}
\caption{层级名称}\label{tab:level-name}
\end{table}
% \marginnote{\linespread{1.1}\small\ttabbox
% {\setcaptiontype{table}
%  \begin{tabular}{r>{\ttfamily}l}
%   \toprule
%   层级 & \multicolumn{1}{c}{名称} \\
%   \midrule
%   -1 & part \\
%   0 & chapter \\
%   1 & section \\
%   2 & subsection \\
%   3 & subsubsection \\
%   4 & paragraph \\
%   5 & subparagraph \\
%   4 & sub3section \\
%   5 & sub4section \\
%   \bottomrule
% \end{tabular}}{\caption{层级名称}\label{tab:level-name}}}[2cm]

\begin{keyval}[path=title/...]{numbering}
  \begin{syntax}
    numbering = <&\TTF> & true 
  \end{syntax}
控制是否对不带星号的命令编号。当此选项设置为 \opt{false} 时，除了不带编号，其余功能均与正常标题一致。

注意，章节是否编号还受到 \texttt{secnumdepth} 计数器的控制，可以通过上述的 
\cs{setsecnumdepth} 命令来设置。例如，对于 \cs{section} 而言，其默认的章节层级为 1
（对于预定义的几个章节标题，其默认的章节层级与同名层级名称的层级相同，
见\cref{tab:level-name}，可通过 \keyref*[title/...]{level} 键来修改层级）。
因此 \cs{section} 会被编号当且仅当 \texttt{secnumdepth}
不小于 1，且其 \opt{numbering} 键为 \opt{true}，并且使用不带星号的章节命令。
\end{keyval}

\begin{keyval}[path=title/...]{name,name-preto,name-appto}
  \begin{syntax}
    name = \{\meta{名字的前半部分},\meta{名字的后半部分}\}
    name = \marg{名字的前一部分}
    name-preto = \marg{pre to}
    name-appto = \marg{app to}
  \end{syntax}
设置章节的名字。所谓“章节的名字”，可以分为前后两部分，即章节编号前后的词语，两个词
之间用一个半角逗号分开；也可以只有一部分，表示只有章节编号之前的名字。

\opt{name-preto} 把 \meta{pre to} 附加到名字之前，\opt{name-appto} 把 \meta{app to} 附加到名字之后。

例如，如果想在某一章节前添加 \verb|*|，只需使用 \verb|name-preto=*|，或 \verb|name-preto=|\allowbreak\verb|\llap{* }|。
\end{keyval}

\begin{keyval}[path=title/...]{number}
  \begin{syntax}
    number = \marg{数字输出命令}
  \end{syntax}
设置章节编号的数字输出格式。\meta{数字输出命令} 通常是对应章节编号计数器的输出命令，如
\verb|\thesection| 或 \verb|\zhnum{chapter}| 之类的。

\opt{number}选项定义的\emph{不会}控制对章节计数器的交叉引用。在引用计数器时，记录在
\LaTeX 辅助文件中的仍然是原来的定义，但可以通过 \keyref*[title/...]{label-format} 修改。
\end{keyval}


\subsection{格式}

和 \CTeX 宏集一样，\CusTeX 也提供了提供了 \opt{numberformat}，\opt{nameformat}，\opt{titleformat}，\opt{format} 这几个选项用来控制章节标题的格式。它们的作用范围如\cref{fig:heading-format} 所示。

% from ctex.dtx
\begin{figure}[htb]
  \centering
  \[
    \underbrace{
      \overbrace{
        \text{\huge\bfseries 第}
        \mspace{-25mu}
        \underbrace{\text{\huge\bfseries 二}}_{\text{\small\opt{numberformat}}}
        \mspace{-25mu}
        \text{\huge\bfseries 章}
      }^{\text{\small\opt{nameformat}}} \quad
      \overbrace{\text{\huge\bfseries 文档接口}}^{\text{\small\opt{titleformat}}}
    }_{\text{\small\opt{format}}}
  \]
  \caption[格式选项的作用范围]{\small\opt{numberformat}，\opt{nameformat}，\opt{titleformat}，\opt{format} 几个选项的作用范围}
  \label{fig:heading-format}
\end{figure}

\begin{keyval}[path=title/...]{format,format+,nameformat,nameformat+,numberformat,numberformat+,titleformat,titleformat+}
  \begin{syntax}
    format  = \marg{格式代码}
    format+ = \marg{格式代码}
  \end{syntax}
设置相应部分的格式。带 \texttt{+} 的用于在原有的格式后增加代码。注意，\texttt{+} 与选项之间不能留有空白，不能写成 \verb|format += {..}|，以下同。
\end{keyval}

\begin{keyval}[path=title/...]{aftername,aftername+,aftertitle,aftertitle+}
  \begin{syntax}
    aftername  = \marg{code}
    aftername+ = \marg{code}
  \end{syntax}
用于将 \meta{code} 插入到相应的部分之后。带 \texttt{+} 的用于在原有的格式后增加代码。
\end{keyval}

\begin{keyval}[path=title/...]{pagestyle}
  \begin{syntax}
    pagestyle = \marg{pagestyle}
  \end{syntax}
\texttt{page} （如 \cs{part}）和 \texttt{top} （如 \cs{chapter}）标题类还可以设置该标题所在页的页面样式。在 \texttt{normal} 标题类中无效。
值为空时，不会修改页面样式。

关于页面样式的相关内容，见\cref{sec:pagestyle}。
\end{keyval}


\subsection{间距和缩进}

\nofuncskip
\begin{keyval}[path=title/...]{runin}
  \begin{syntax}
    runin = <&\TTF> & false 
  \end{syntax}
用于指定是否是标题与其后的文字排在同一行。仅对 \texttt{normal} 类有效。
\end{keyval}

\begin{keyval}[path=title/...]{hang}
  \begin{syntax}
    hang = <&\TTF> & false 
  \end{syntax}
设置是否对章节标题实施悬挂缩进（缩进的宽度为名字宽度和 indent 选项设
置的宽度之和）。设置该选项为 \opt{true} 时必须恰当地设置 \opt{aftername} 选项。

若设置了 \opt{runin} 为 \opt{true}，则该选项无意义。
\end{keyval}

\begin{keyval}[path=title/...]{indent}
  \begin{syntax}
    indent = \marg{缩进间距}
  \end{syntax}
设置章节标题本身的首行缩进。如果这缩进值是相对单位，则缩进间距的大小是相对于 \opt{format}
中指定的字号大小。
\end{keyval}

\begin{keyval}[path=title/...]{beforeskip,afterskip,leftskip,rightskip}
  \begin{syntax}
    beforeskip = \marg{skip expr}
  \end{syntax}
设置章节标题前后左右的垂直间距。若 \opt{runin} 选项为 \opt{true}，则设置的是水平间距。

其中，左右间距只在某些类中有效。
\end{keyval}

\begin{keyval}[path=title/...]{fixskip}
  \begin{syntax}
    fixskip = <&\TTF> & false 
  \end{syntax}
默认情况下，章节标题除了由 \opt{beforeskip} 和 \opt{afterskip} 选项设置的垂直间距外，还会有其它一些多余的间距，\opt{fixskip} 用于指定是否抑制这些多余的间距。
\end{keyval}

\begin{keyval}[path=title/...]{ensureskip}
  \begin{syntax}
    ensureskip = <&\TTF> & false 
  \end{syntax}
使用某些标题类时，标题如果出现在新的一页，\opt{beforeskip} 可能并不一定准确，可以使用此选项
以确保 \opt{beforeskip} 有准确的值。
\end{keyval}

\begin{keyval}[path=title/...]{break,break+}
  \begin{syntax}
    break  = \marg{code}
    break+ = \marg{code}
  \end{syntax}
\opt{break} 选项用于控制章节标题与之前正文的分隔关系。一般用于设置是否在标题之前分页或
者设置行间罚点。
\end{keyval}

例如，若当前页剩余高度小于正文高度的一半时，则另起一页输出 \cs{section} 标题：

\begin{xample}
\usepackage{needspace}
\setuptitle [section] { break = \Needspace{0.5\textheight} }
\stopxamplecode
\xamplecode
\medskip
\end{xample}

\begin{keyval}[path=title/...]{afterindent}
  \begin{syntax}
    afterindent = <&\TTF>
  \end{syntax}
\opt{afterindent} 选项用于设置章节标题后首段的缩进。
\end{keyval}

\subsection{浮动体}

\CusTeX 提供了可以控制本章节内的浮动体位置的接口。

\begin{keyval}[path=title/...]{float-barrier}
  \begin{syntax}
    float-barrier = <&\TTF> & false 
  \end{syntax}
控制是否本章节所属的浮动体可以位于其它章节内，为 \opt{true} 时，浮动体不能放在其它章节内。
默认情况下浮动体可以放置在其它章节内。
\end{keyval}

除了使用以上这个选项，还可以设置浮动体的“边界”。

\begin{function}{\FloatBarrier}
阻止 \cs{FloatBarrier} 前的浮动体被放置在这个命令的后边。
\end{function}

\subsection{杂项}

\nofuncskip
\begin{keyval}[path=title/...]{beforerecord,beforerecord>,beforerecord<}
  \begin{syntax}
    beforerecord = \marg{code}
  \end{syntax}
在写入辅助文件之前执行一些必要的设置。例如，如果需要保存 \opt{xpos} 和 \opt{ypos}，
必须要先执行 \tn{pdfsavepos}，则可以使用该键。
\end{keyval}

\begin{keyval}[path=title/...]{tocline}
  \begin{syntax}
    tocline = \marg{格式定义}
  \end{syntax}
定义章节标题在目录文件中的格式，\meta{格式定义} 有两个参数：参数
\verb|#1| 是 \texttt{part}、\opt{chapter} 等名字，参数 \verb|#2| 是标题内容。

初始值是：\verb|\titlenumberline{#1}#2|。其含义为，若标题没有名字，则不输出编号。
\end{keyval}

\begin{keyval}[path=title/...]{mark}
  \begin{syntax}
    mark = \marg{mark code}
  \end{syntax}
写入标记文本。\meta{mark code} 其后跟一个参数，在 \cs{chapter} 中，为 \tn{chaptermark}，
在 \cs{section} 中，为 \tn{sectionmark}。初始情况下，不写入标记。
\end{keyval}

\begin{keyval}[path=title/...]{properties,properties+}
  \begin{syntax}
    properties = \marg{properties clist}
  \end{syntax}
使用 \cs{RecordProperties} 向 \texttt{.aux} 文件写入辅助信息。

写入的 \veta{label} 名为 \texttt{cus.title.}\cs{thetitlecount}。

可以使用 \verb|\RefProperty{cus.title.|\cs{thetitlecount}\verb|}|\marg{property} 获得
在当前标题下写入的 \meta{property} 的值。
\end{keyval}

\begin{keyval}[path=title/...]{bookmark,bookmark*,bookmark-extra}
  \begin{syntax}
    bookmark  = \marg{text}
    bookmark* = \marg{text}
    bookmark-extra = \marg{text}
    bookmark-extra = \oarg{bookmark options} \marg{text}
  \end{syntax}
设置\emph{此条}标题在书签中的文字。

\opt{bookmark} 会在 \pkg{bookmark} 宏包设置了 \opt{numbered} 选项后，把数字也加上。
\linebreak\opt{bookmark*} 则直接设置书签为 \meta{text}。

\opt{bookmark-extra} 额外添加一个书签。
用在带星号的命令中可以为其增加书签。
\end{keyval}

\begin{function}{\titleaddinfo,\titleremoveinfo,\titleremoveallinfo}
  \begin{syntax}
    \V\titleaddinfo   \marg{property} \marg{value}
    \V\titleaddinfo * \marg{property} \marg{value}
    \V\titleremoveinfo  \marg{property} 
    \V\titleremoveinfo * \marg{property} 
    \V\titleremoveallinfo 
    \V\titleremoveallinfo *
  \end{syntax}
为下一个（或多个）标题添加（或移除）额外的信息，以 \meta{property} 标识。

当 \verb|\titleaddinfo| 和 \verb|\titleaddinfo*| 有同一个 \meta{property} 时，
不带星号的具有更高优先级。
\end{function}

\begin{keyval}[path=title/...]{style}
  \begin{syntax}
    style = \marg{style}
  \end{syntax}
设置已有的样式。
\end{keyval}

\begin{keyval}[path=title/...]{mode}
  \begin{syntax}
    mode = <&normal|nonumber|starred|...>
  \end{syntax}
设置标题的模式。具体效果取决于标题类的定义。

带星号的标题相当于 \verb|mode=starred|，但带星号的命令优先级更高，设置 \opt{mode}
不能取消带星号的效果。

\verb|numbering=true| 相当于 \verb|mode=normal|，\verb|numbering=false| 相当于 
\verb|mode=nonumber|。
\end{keyval}

\begin{function}[EXP]{\titleifnamed}
  \begin{syntax}
    \verb|\titleifnamed| \marg{有名字时的内容} \marg{无名字时的内容}
  \end{syntax}
根据当前章节有无名字展开得到不同内容（通常是格式命令）。
\end{function}

\begin{function}[EXP]{\ifintitle}
  \begin{syntax}
    \V\ifintitle \marg{在标题中执行} \marg{不在标题中执行}
  \end{syntax}
根据是否在标题中执行相应代码。从正要设置键值之前到恢复键值原来的定义之间这段代码都算作在标题中。另见 \cs{ifincblentry}。
\end{function}

\begin{function}[EXP]{\ifincblentry}
  \begin{syntax}
    \V\ifincblentry \marg{写入目录条目时} \marg{非写入目录条目时}
  \end{syntax}
判断是否正在写入目录条目。
\end{function}

每一个标题都有一个对应的 \texttt{\textbackslash titlethe\meta{title}} 命令，表示当前实际输出的章节标题的名字。如现在 \verb|\titlethechapter| 为 “\titlethechapter”。


\subsection{目录}\label{sec:struct-cbl-short}

\CusTeX 重新实现了目录的制作方式，将每个目录项看成是一个个数据，不同于标准的目录制作方式，因此可能存在与其它宏包不兼容的情况。

在 \CusTeX 中，目录被称为 “combined list”，这是 \ConTeXt 中的称呼。

\CusTeX 的 \cusmodule{struct} 一共定义了三种格式的目录：plain、template、specified。
这三种目录格式可定制程度依次增加，使用方式也依次更加复杂。

\begin{function}{\enablecombinedlist}
\cs{enablecombinedlist} 启用目录。可用于导言区或文档最开头。如果未使用此命令则其它目录命令不可用。
\end{function}

\begin{keyval}[path=cbl-setup]{from}
  \begin{syntax}
    from = \marg{file} & \V\jobname
  \end{syntax}
设置目录的来源。默认为 \tn{jobname}，即来自本文件。不包含文件后缀。
\end{keyval}

\begin{keyval}[path=cbl-setup]{write,to}
  \begin{syntax}
    write = <&\TTF> & true 
    to    = \marg{file}
  \end{syntax}
控制当前主文件是否写入目录，及写入到哪个文件。如果设置了 \opt{to}，则写入到 \meta{file}\texttt{.toc}，否则写入 \opt{from} 键指定的那个文件。
\end{keyval}

\pkg{cus} 提供了与 \cls{book} 类相似的目录格式，这种目录称为 ``plain'' 格式的目录。

当 \tn{contentsname}、\tn{listfigurename}、\tn{listtablename} 为空时，对应的 
plain 格式目录不会附带章节标题，否则将自动添加一个正常的带序号的\emph{章}标题。
它们可以通过 \cs{settocdepth} 控制输出的层级。

\begin{function}{\tableofcontents,\listoffigures,\listoftables,\localtableofcontents}
\begin{syntax}
  \V\tableofcontents      \oarg{multicolumns keyval}
  \V\localtableofcontents \oarg{multicolumns keyval} \string(<level>,<index>\string)
\end{syntax}
输出 plain 格式的标题、图片和表格目录。

\cs{localtableofcontents} 输出局部的标题目录，层级为 \meta{level}，位置为 \meta{index}。
\meta{level} 用于设置局部目录的层级，例如为 \texttt{chapter} 时，输出本章目录。
默认为最近使用的那个标题的层级。
\end{function}

如下代码输出本小节的目录：
\begin{xample}
\renewcommand\contentsname{} % 移除自动添加的标题
\localtableofcontents(section) % 输出本节目录，本 \section 也会输出
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\multicolplaincombinedlist}
  \begin{syntax}
    \verb|\multicolplaincombinedlist| \oarg{multicolumns key-val} \marg{title} \marg{cbl type}
  \end{syntax}
\cs{multicolplaincombinedlist} 输出 plain 格式的多栏目录，该目录的类型是 \meta{cbl type}，
并以 \meta{title} 作为标题。如果 \meta{title} 为空，则不输出标题。
\meta{multicolumns key-val} 设置多栏选项，如果栏数为 1，则相当于默认的单栏目录。
\end{function}

\begin{function}{\multicollocalplaincombinedlist}
  \begin{syntax}
    \V\multicollocalplaincombinedlist \oarg{multicolumns keyval}
    ~~~~\marg{title} \marg{cbl type} \marg{min} \marg{max}
  \end{syntax}
输出多栏局部目录。局部目录从 \meta{min} 开始，到 \meta{max} 终止。如果 \meta{title} 为空，则不输出标题。

实际上，\cs{localtableofcontents} 根据 \meta{level} 和 \meta{index} 隐式计算了 \meta{min} 和 \meta{max}。

可以使用 \cs{getcbllevelrange} 获得对应位置小节目录的范围 \meta{min}、\meta{max}。
\end{function}

\pkg{cus} 提供了设置用键值对目录进行简易调整的目录格式，称为 ``template'' 格式的目录。

当 \tn{contentsname}、\tn{listfigurename}、\tn{listtablename} 为空时，对应的 
template 格式目录不会附带章节标题，否则将自动添加一个正常的带序号的\emph{章}标题。
它们可以通过 \cs{settocdepth} 控制输出的层级。

\begin{function}{\templatetoc,\templatelof,\templatelot}
  \begin{syntax}
    \V\templatetoc
    \V\templatetoc \oarg{templatecbl keys} \oarg{multicolumns key} \string(\meta{level},\meta{index}\string)
  \end{syntax}
输出 ``template'' 格式的目录。\meta{templatecbl keys} 用于局部修改目录的输出。
\end{function}

\begin{function}{\templatecombinedlist}
  \begin{syntax}
    \V\templatecombinedlist \marg{type} \marg{title}
    \V\templatecombinedlist \oarg{templatecbl keys} \oarg{multicolumns key} \marg{cbl type} 
    ~~\string(\meta{level},\meta{index}\string) \marg{title}
  \end{syntax}
template 格式目录的完整形式。
\end{function}

下面列出 \meta{templatecbl keys} 可用的键值选项，它们只能在 
\cs{templatetoc} 等命令内部使用，不能在外部的 \cs{cussetup} 中设置。

\begin{keyval}[path=templatecbl]{hide,show}
  \begin{syntax}
    hide = \marg{cbl level clist}
  \end{syntax}
隐藏或显示 \meta{cbl level clist} 指定的目录层级。
\end{keyval}

\begin{keyval}[path=templatecbl]{*}
  \begin{syntax}
    * = \marg{template keys}
  \end{syntax}
把 \meta{template keys} 应用到当前目录类型的所有目录层级。
\meta{template keys} 是下文将要说明的模板键。
\end{keyval}

\begin{keyval}[path=templatecbl]{no-parent}
  \begin{syntax}
    no-parent &&
  \end{syntax}
移除局部目录的第一项。

对于一个局部目录，如输出本章目录（\texttt{chapter} 层级），
不仅会输出本章的小节写入的目录条目，还会输出章（即 \tn{chapter}）写入的目录内容，
使用此选项将不会输出章的条目。
\end{keyval}

对于没有定义的 \veta{templatecbl key}，首先会判断它是不是以 \texttt{*} 开头，
若是，则用该键的值修改这个 \texttt{*} 后面的大括号中的那些目录实例。
否则，尝试将它解释为一个 \texttt{templatecbl} 目录对象的一个实例名，
然后使用该键给出的值修改此实例。如果也不是一个实例名，则会报错。
关于目录对象和目录实例，见下文。

\begin{xample}
\renewcommand\contentsname{} % 移除自动添加的标题
\templatetoc[no-parent,*{subsection}={ignore=true},
  section={ignore=false}](chapter) % 输出本章目录，但隐藏 \chapter 和 \subsection
\stopxamplecode
\xampleprint
\end{xample}

template 格式的目录，使用 \pkg{lttemplates} 宏包处理键值。
一个模板的对象类型用于规定的参数的数量，
一个对象类型可以定义多个模板，每个模板有自己的处理代码和可用的键，
在模板的基础上为一些键设置固定的值就构成了对象类型的实例。
对象类型的实例总是唯一的，而不论它是设置了哪个模板的键。

template 格式的目录，其对象类型为 \texttt{templatecbl}，可使用 7 个参数，为目录条目
所保存的值。默认的模板为 \verb*|by name|，实例为目录条目的层级，如 \texttt{part}、
\texttt{chapter} 等等。用户可以自己定义新的模板，详见\cref{sec:template-cbl}。
关于如何修改定义模板、修改实例等见\cref{sec:lttemplates}或 \file{lttemplates.pdf}。

\begin{figure}[ht]
\includegraphics[width=\linewidth]{templatetoc-para}
\caption{template 格式目录的变量}
\end{figure}

\texttt{by name} 模板可用的键如下（注意它们不能用 \cs{cussetup} 设置，且每个键都必须给出
值，不能省略，包括布尔值的 \texttt{true} 也不能省，这是 \pkg{lttemplates} 所限）：
\begin{description}
  \item[\texttt{space.before}] 如果不是为 \texttt{0pt}，且在垂直模式，则在该目录项之前添加指定的间距；结果保存于 \tn{tmcbl@beforeskip} 寄存器之中；
  \item[\texttt{space.left}] 左侧间距；结果保存于 \tn{tmcbl@leftskip} 寄存器之中；
  \item[\texttt{space.right}] 右侧间距，默认情况下，页码会嵌入到右侧空白之中；结果保存于 \tn{tmcbl@right} 寄存器之中；
  \item[\texttt{space.hang}] 第一行额外的缩进，正值向左侧缩进，负值向右侧缩进；结果保存于 \tn{tmcbl@hang} dim 寄存器之中；默认为 \texttt{width.name} 的值；
  \item[\texttt{space.indent}] 除第一段外，每段首行额外的缩进；结果保存于 \tn{tmcbl@indent} dim 寄存器之中；默认为 \texttt{space.hang} 的值；
  \item[\texttt{penalty.before}] 增加 penalty；结果保存于 \tn{tmcbl@beforepenalty} int 寄存器之中；
  \item[\texttt{format}] 设置标题前的数字（即 \texttt{name}）、标题文字（即 \texttt{title}）、引导线（即 \texttt{leader}）和页码（即 \texttt{page}）的格式；结果保存于 \tn{tmcbl@format} 宏之中；
  \item[\texttt{format.name}] 设置 \texttt{name} 的格式，若样式没有覆盖，\texttt{format} 所设仍会生效；结果保存于 \tn{tmcbl@nameformat} 宏之中；
  \item[\texttt{format.title}] 设置 \texttt{title} 的格式，若样式没有覆盖，\texttt{format} 所设仍会生效；结果保存于 \tn{tmcbl@titleformat} 宏之中；
  \item[\texttt{format.page}] 设置 \texttt{page} 的格式，若样式没有覆盖，\texttt{format} 所设仍会生效；结果保存于 \tn{tmcbl@pageformat} 宏之中；
  \item[\texttt{width.name}] 设置 \texttt{name} 的宽度，具体效果如何取决于 \texttt{code.name}；结果保存于 \tn{tmcbl@namewidth} dim 寄存器之中；
  \item[\texttt{width.title}] 设置 \texttt{title} 的宽度，具体效果如何取决于 \texttt{code.title}；结果保存于 \tn{tmcbl@titlewidth} dim 寄存器之中；
  \item[\texttt{width.page}] 设置 \texttt{page} 的宽度，具体效果如何取决于 \texttt{code.page}；结果保存于 \tn{tmcbl@pagewidth} dim 寄存器之中；默认为 \verb|\@pnumwidth|；
  \item[\texttt{width.line}] 设置 \tn{tmcbl@linewidth} dim 寄存器；
  \item[\texttt{ignore}] 是否忽略该层级的目录项；结果保存于 \tn{tmcbl@ignore}，可使用 \tn{ifodd} 或 \cs{bool_if:NTF} 等判断；
  \item[\texttt{name.width}] 同 \texttt{width.name}；
  \item[\texttt{name.format}] 同 \texttt{format.name}；
  \item[\texttt{name.code}] 同 \texttt{code.name}；
  \item[\texttt{title.width}] 同 \texttt{width.title}；
  \item[\texttt{title.format}] 同 \texttt{format.title}；
  \item[\texttt{title.code}] 同 \texttt{code.title}；
  \item[\texttt{page.width}] 同 \texttt{width.page}；
  \item[\texttt{page.format}] 同 \texttt{format.page}；
  \item[\texttt{page.code}] 同 \texttt{code.page}；
  \item[\texttt{leader.code}] 同 \texttt{code.leader}；
  \item[\texttt{leader.sep}] 设置引导线的内容的间距；最终结果保存于 \tn{tmcbl@leadersep} 宏之中，默认为 4.5；当大于 5000 时，不使用引导线，而使用 \cs{filler}；
  \item[\texttt{leader.options}] 引导线使用 \cs{filler} 时的选项；结果保存于 \tn{tmcbl@leaderoptions} 宏之中；默认为 \texttt{space}；
  \item[\texttt{leader.content}] 设置引导线的内容；结果保存于 \tn{tmcbl@leadercontent} 宏之中；默认为西文句点 \texttt{.}；
  \item[\texttt{hyper.range}] 设置超链接要包含的内容，可选值为 \texttt{name}、\texttt{title}、\texttt{page}、\texttt{none} 和 \texttt{all}，可多选；最终结果保存于 \tn{tmcbl@hyperrange} int 寄存器之中，其中 \texttt{name} 将其加 1，\texttt{title} 将其加 2，\texttt{page} 将其加 4；默认为 \texttt{page}；
  \item[\texttt{hyper.code}] 同 \texttt{code.hyper}；
  \item[\texttt{code}] 将该目录层级要执行的代码替换为它，可使用 7 个参数，即目录条目保存的值；将结果保存在 \tn{tmcbl@code} 命令之中；
  \item[\texttt{code.name}] 将 \texttt{name} 执行的代码替换为它，结果保存在 \tn{tmcbl@name} 命令之中；
  \item[\texttt{code.title}] 将 \texttt{title} 执行的代码替换为它，结果保存在 \tn{tmcbl@title} 命令之中；
  \item[\texttt{code.leader}] 将 \texttt{leader} 执行的代码替换为它，结果保存在 \tn{tmcbl@leader} 命令之中；
  \item[\texttt{code.page}] 将 \texttt{page} 执行的代码替换为它，结果保存在 \tn{tmcbl@page} 命令之中；
  \item[\texttt{code.hyper}] 将添加超链接要执行的代码替换为它，可使用 4 个参数，分别为 \texttt{name}、\texttt{title}、\texttt{leader} 和 \texttt{page} 要执行的代码，结果保存在 \tn{tmcbl@hyper} 命令之中；
  \item[\texttt{code.before}] 在目录条目之前执行代码；
  \item[\texttt{code.after}] 在目录条目之后执行代码，默认为 \tn{par}；注意每个目录项不会自动另起一行，依靠它来另起新行。
\end{description}
在 \texttt{code.name}、\texttt{code.title} 等代码中可以使用上述保存的寄存器（或宏），
若在其它地方使用它们则不保证其有正确结果。此外，在这些代码中还可以使用下述宏\zhslash 命令：
\begin{description}
  \item[\tn{tmcblthetype}] 当前使用的目录类型；
  \item[\tn{tmcbltheclass}] 当前目录层级名；
  \item[\tn{tmcblthelevel}] 当前目录层级对应的整数；
  \item[\tn{tmcblretinfo}]\marg{key} 返回此目录条目中 \meta{key} 键保存的额外信息，若不存在则为空；
  \item[\tn{tmcbltheinfo}] 此目录条目保存的额外信息，为一个 \LaTeX3 prop 变量，即 \cs{titleaddinfo} 添加的值；
  \item[\tn{tmcblthename}] \texttt{name} 的文字，可能为空；
  \item[\tn{tmcblthetitle}] \texttt{title} 的文字；
  \item[\tn{tmcblthepage}] \texttt{page} 的文字，即 \tn{thepage} 的值；
  \item[\tn{tmcbltheanchor}] 超链接的锚点，可能为空；可直接使用 \tn{tmcbl@hyperlink} 或 \tn{tmcbl@hyperlinkbox}添加超链接；
  \item[\tn{tmcbl@code@}]\marg{type}\marg{count}\marg{infos}\marg{level int}\marg{cbl list entry}\marg{thepage}\marg{anchor} 默认的 \texttt{code} 实现；
  \item[\tn{tmcbl@name@}] 默认的 \texttt{code.name} 实现；
  \item[\tn{tmcbl@title@}] 默认的 \texttt{code.title} 实现；
  \item[\tn{tmcbl@leader@}] 默认的 \texttt{code.leader} 实现；
  \item[\tn{tmcbl@page@}] 默认的 \texttt{code.page} 实现；
  \item[\tn{tmcbl@hyper@}]\marg{name}\marg{title}\marg{leader}\marg{page} 默认的 \texttt{code.hyper} 实现；
  \item[\tn{tmcbl@hyperlink}]\marg{text} 为 \texttt{text} 添加链接到正文标题处的超链接；
  \item[\tn{tmcbl@hyperlinkbox}]\marg{text} 为 \texttt{text} 添加链接到正文标题处的超链接；
\end{description}

仿照 \pkg{etoc} 宏包，提供了类似于 \tn{etocsetstyle} 的命令，这种目录称为 ``specified'' 目录。
\begin{function}{\tocsetstyle,\lotsetstyle,\lofsetstyle,
  \specifiedtoc,\specifiedlot,\specifiedlof,
  \localspecifiedtoc}
  \begin{syntax}
    \V\tocsetstyle \marg{level clist} \marg{list start} \marg{block start} \marg{block item}
    ~~~~\marg{block finish} \marg{list finish}
    \V\lotsetstyle \marg{list start} \marg{block start} \marg{block item} \marg{block finish} \marg{list finish}
    \V\lofsetstyle \marg{list start} \marg{block start} \marg{block item} \marg{block finish} \marg{list finish}
    \V\specifiedtoc 
    \V\localspecifiedtoc 
    \V\localspecifiedtoc \string(\meta{level},\meta{index}\string)
  \end{syntax}
设置章节目录、表格目录、图片目录的样式。\cs{specified...} 则用于输出指定的目录。
\cs{localspecifiedtoc} 用于输出局部章节的目录。

详细用法见\cref{ch:title-cbl}。
\end{function}

\begin{function}{\SaveSpecifiedCombinedListStyle}
  \begin{syntax}
    \V\SaveSpecifiedCombinedListStyle \marg{cmd} \marg{style code}
  \end{syntax}
把 \meta{style code} 中设置的样式保存在 \meta{cmd} 中。这些样式是由 \cs{tocsetstyle}、
\cs{Set\-SpecifiedCombinedListStyle} 等命令设置的。当要保存其它内容到 \meta{cmd} 中时，
可以使用 \cs{+} 命令。

相比把 \cs{tocsetstyle} 等直接保存到 \meta{cmd} 中，在实际使用 \meta{cmd} 时，
还会有其它处理，而使用 \cs{SaveSpecifiedCombinedListStyle} 保存则不会有多余的处理。

类似 \cs{keys_precompile:nnN} 之于 \cs{keys_set:nn}。
\end{function}

如下代码将一段样式设置保存在 \verb|\mysavedtocstyle| 中，还保存了 \\
\verb|\newcommand{...}|。
\begin{xample}
\SaveSpecifiedCombinedListStyle \mysavedtocstyle {
  \tocsetstyle 
    {chapter,section,subsection}
    {\begin{description}}
    {}
    {\item[\mytoclabel]\tocthetitle\quad\toclink{\tocthepage}\par}
    {}
    {\end{description}}
  \+{\newcommand{\mytoclabel}
      {\tocifnamed{\tocthename}{\rule{1ex}{1ex}}}}
}
\stopxamplecode
\xamplecode\medskip
\end{xample}

\begin{function}{\SetSpecifiedCombinedListStyle,\SpecifiedCombinedList,
  \LocalSpecifiedCombinedList}
  \begin{syntax}
    \V\SetSpecifiedCombinedListStyle \oarg{type clist} \marg{level clist}
    ~~~~\marg{list start} \marg{block start} \marg{block item} \marg{block finish} \marg{list finish}
    \V\SpecifiedCombinedList \oarg{cbl type}
    \V\LocalSpecifiedCombinedList \oarg{cbl type}
    \V\LocalSpecifiedCombinedList \oarg{cbl type} \string(\meta{level},\meta{index}\string)
  \end{syntax}
完整形式。详细用法见\cref{ch:title-cbl}。
\end{function}

关于目录的内部数据结构，见\cref{sec:struct-programming}和\cref{ch:title-cbl}。

关于章节标题和目录的详细用法和样例，见\cref{ch:title-cbl}。

%region buffer, TODO
\section{\cusmodule{buffer}模块}
未完成。\TODO 

% \cusmodule{buffer} 用于提供缓存机制，将内容保存起来，以便后续使用，例如 verbatim。

% \begin{function}{\startbuffer,\stopbuffer}
% \begin{syntax}
%   \verb|\startbuffer| \oarg{key-vals}
%   ~~<content>
%   \verb|\stopbuffer|
% \end{syntax}
% 保存 \meta{content}。

% 可以嵌套使用，但不能用于环境以及命令的定义中。要创建这样的命令，应使用键值参数或
% \csreflist{newbuffer,newverbbuffer} 等。
% \end{function}

% \begin{function}{\newbuffer,\newverbbuffer}
% \begin{syntax}
%   \verb|\newbuffer|   \oarg{key-vals} \meta{start macro} \meta{stop macro}
%   \verb|\newbuffer| * \oarg{key-vals} \meta{start macro} \meta{stop macro} \marg{begin} \marg{end}
%   \verb|\newverbbuffer| \oarg{key-vals} \meta{start macro} \meta{stop macro}
% \end{syntax}
% \cs{newbuffer} 定义新的 buffer 命令，这样的 buffer 不是 verbatim 的。

% \cs{newverbbuffer} 定义新的 buffer 命令，这样的 buffer 是 verbatim 的。
% \end{function}

% \begin{keyval}[path=buffer]{mode,mode*}
% \begin{syntax}
%   mode  = <&set|push|macro|seq|write> & set 
%   mode* = <{modes}>
% \end{syntax}
% buffer 的保存模式。
% \end{keyval}

% \begin{function}{\getbuffer,\setfrombuffer}
% \begin{syntax}
%   \verb|\getbuffer|   \marg{buffer name}
%   \verb|\getbuffer|   \marg{buffer name} \parg{索引}
%   \verb|\getbuffer| * \marg{buffer name}
%   \verb|\setfrombuffer|   \marg{buffer name}         \meta{macro}
%   \verb|\setfrombuffer|   \marg{buffer name} \parg{索引} \meta{macro}
%   \verb|\setfrombuffer| * \marg{buffer name}         \meta{macro}
% \end{syntax}
% 使用、获取 buffer。
% \end{function}
%endregion


\chapter{编程接口}

本章描述 \CusTeX 提供的编程接口。

\begin{function}[module=cus]{\CUSProvideLibrary,\CUSProvideExplLibrary}
\begin{syntax}
  \verb|\CUSProvideLibrary|     \marg{库名} \oarg{描述}
  \verb|\CUSProvideExplLibrary| \marg{库名} \marg{日期} \marg{版本} \marg{描述}
\end{syntax}
提供库文件。库文件名必须是：“\opt{cus.library.\meta{库名}.tex}”。
\end{function}

\begin{function}[module=cus]{\CUSLibraryDelayedUntil}
  \begin{syntax}
    \V\CUSLibraryDelayedUntil   \marg{package}
    \V\CUSLibraryDelayedUntil * \{\}
    \V\CUSLibraryDelayedUntil * \marg{package}
  \end{syntax}
用于库文件的第一行，将本库标记为需要延迟加载。

标记本库延迟至加载了 \meta{package} 之后再加载，
带星号的命令将库延迟至加载了文档类之后再加载。
若有其它库依赖使用了本命令的库，则它们都至少会延迟至 \meta{package} 之后再加载。
\end{function}

\begin{function}[module=cus]{\CUSDependency}
\begin{syntax}
  \verb|\CUSDependency|   \marg{key-val}
  \verb|\CUSDependency| * \marg{key-val}
\end{syntax}
处理库依赖。

若用在主文件中或使用带星号的版本，所需的依赖会立刻被加载。用在库文件中，依赖可能会延迟加载。
\end{function}

\begin{keyval}[path=dependency]{package,module,library,disable}
\begin{syntax}
  package = \marg{clist}
  module  = \marg{clist}
  library = \marg{clist}
  disable = \marg{clist}
\end{syntax}
\cs{CUSDependency} 可用的键值选项。
\end{keyval}

\begin{texnote}
只能以如下顺序执行上述三个命令：\cs{CUSLibraryDelayedUntil}、\linebreak
\cs{CUSDependency}、\cs{CUSProvideExplLibrary}（或 \cs{CUSProvideLibrary}）。
在每个库文件中，它们最多只能使用一次，且必须用在库文件开头。
若要在库文件中间使用它们，则应该将之后的内容拆分至新的库文件中。

一个库文件可能会被使用多次，但在 \cs{CUSProvideExplLibrary}（或 \cs{CUSProvideLibrary}） 
之后的内容只会执行一次。
\end{texnote}

\begin{function}[module=cus]{\CUSLoadLibrary,\CUSPassOptionsToLibrary}
\begin{syntax}
  \V\CUSLoadLibrary \oarg{选项} \marg{库名} \oarg{日期}
  \V\CUSPassOptionsToLibrary \marg{选项} \marg{库名}
\end{syntax}
尽量不要使用 \cs{CUSLoadLibrary}，它可能不会正确处理库依赖。
\end{function}

\begin{function}[EXP]{\text_mdfive_hash:n}
  \begin{syntax}
    \V*|\text_mdfive_hash:n| \marg{text}
  \end{syntax}
先使用 \cs{text_expand:n} 展开 \meta{text}，然后计算它的 MD5 值。
\end{function}


\section{\LaTeXe 的钩子机制}\label{sec:lthooks}

本节简述 \LaTeXe 的钩子机制。更详细的说明见 \file{lthooks-doc.pdf}。

“钩子”（hook）是在命令或环境的定义中可以添加其它代码的位置。

\begin{function}[module=hook]{\UseHook,\UseHookWithArguments}
  \begin{syntax}
    \verb|\UseHook|              \marg{hook}
    \verb|\UseHookWithArguments| \marg{hook} \marg{number} \marg{arg_1} ... \marg{arg_n}
  \end{syntax}
在命令或环境中执行 \meta{hook}。
\end{function}

\begin{function}[module=hook]{\AddToHook,\AddToHookWithArguments}
  \begin{syntax}
    \verb|\AddToHook|              \marg{hook} \oarg{label} \marg{code}
    \verb|\AddToHookWithArguments| \marg{hook} \oarg{label} \marg{code}
  \end{syntax}
将 \meta{code} 添加到 \meta{hook} 中，标记为 \meta{label}。\meta{hook} 可以未被定义。

如果未指定 \meta{label}，则使用默认的 label。如果 \cs{AddToHook} 用在宏包或文档类中，
它就是宏包或文档类名，否则，它是 \texttt{top-level}。
\end{function}

\begin{function}[module=hook]{\RemoveFromHook}
  \begin{syntax}
    \verb|\RemoveFromHook| \marg{hook} \oarg{label}
  \end{syntax}
移除标记了 \meta{label} 的 \meta{hook}。若 \meta{label} 未指定，则使用默认的 label。
\end{function}

\begin{function}[module=hook]{\AddToHookNext,\AddToHookNextWithArguments,\ClearHookNext}
  \begin{syntax}
    \verb|\AddToHookNext|              \marg{hook} \marg{code}
    \verb|\AddToHookNextWithArguments| \marg{hook} \marg{code}
    \verb|\ClearHookNext| \marg{hook}
  \end{syntax}
将 \meta{code} 添加到 \meta{hook} 的下一次调用中。在正常的 \meta{hook} 代码执行完毕后再执行 \meta{code}。
\end{function}


\section{\LaTeXe 的模板机制}\label{sec:lttemplates}

内容、排布内容的方式以及具体的配置项构成了一个“文档”。
其中内容为用户输入，排布内容的方式一般是一个环境或命令，具体的可配置项就是命令的某些参数
或者可供修改的变量。其中用户输入是未知的，而排布内容的方式和修改配置的方式则是固定的，
且有多种实现方式，模板机制就是其中一种。

本节内容简要介绍 \LaTeXe 的模板机制，更详细的用法请参考 \file{lttemplates-doc.pdf}。

对象类型（object type）是对用户输入的抽象描述，它限定了用户输入参数的数目，一般也限定了顺序。
例如，对于一个章节标题来说，用户输入参数可能是：短标题、完整标题。
\begin{function}[module=lttemplates]{\NewTemplateType}
  \begin{syntax}
    \V\NewTemplateType \marg{type} \marg{no. of args}
  \end{syntax}
定义一个对象类型 \meta{type}。
\end{function}

模板规定了如何处理可配置的项和用户输入的参数，亦即排布内容的方式。
\begin{function}[module=lttemplates]{\DeclareTemplateInterface}
  \begin{syntax}
    \V\DeclareTemplateInterface \marg{type} \marg{template} \marg{no. of args} \marg{key list}
  \end{syntax}
\LaTeXe 模板机制以键值对的方式处理可配置的项，
本命令用于限定用户可配置哪些项，以及用何种方式保存这些项，以及这些项的默认值。
\end{function}

\begin{function}[module=lttemplates]{\DeclareTemplateCode}
  \begin{syntax}
    \V\DeclareTemplateCode \marg{type} \marg{template} \marg{no. of args}
    ~~\marg{key bindings} \marg{code}
  \end{syntax}
定义可配置项与哪一命令绑定起来，以及排布内容的方式。
\end{function}

\begin{function}[module=lttemplates]{\UseTemplate}
  \begin{syntax}
    \V\UseTemplate \marg{type} \marg{template} \marg{settings} \meta{arguments}
  \end{syntax}
把配置项设置为某些固定的值，并立刻使用它。
\end{function}

\begin{function}[module=lttemplates]{\SetTemplateKeys}
  \begin{syntax}
    \V\SetTemplateKeys \marg{type} \marg{template} \marg{keyvalues}
  \end{syntax}
用于在模板的代码中修改实例的值。
\end{function}

\begin{function}[module=lttemplates]{\EditTemplateDefaults}
  \begin{syntax}
    \V\EditTemplateDefaults \marg{type} \marg{template} \marg{new defaults}
  \end{syntax}
修改模板的默认值。只会应用到新创建的实例。
\end{function}

用某一模板把可配置项设置为某些特定的值就成为了实例。
\begin{function}[module=lttemplates]{\DeclareInstance,\DeclareInstanceCopy}
  \begin{syntax}
    \V\DeclareInstance \marg{type} \marg{instance} \marg{template} \marg{parameters}
    \V\DeclareInstanceCopy \marg{type} \marg{instance_2} \marg{instance_1}
  \end{syntax}
使用 \meta{parameters} 创建 \meta{template} 的实例。
或创建 \meta{instance_1} 的复本 \meta{instance_2}。
\end{function}

\begin{function}[module=lttemplates]{\UseInstance}
  \begin{syntax}
    \V\UseInstance \marg{type} \marg{instance} \meta{arguments}
  \end{syntax}
使用实例。
\end{function}

\begin{function}[module=lttemplates]{\EditInstance}
  \begin{syntax}
    \V\EditInstance \marg{type} \marg{instance} \marg{new values}
  \end{syntax}
修改已有的实例。
\end{function}

\begin{function}[module=lttemplates]{\IfInstanceExistsTF,\IfInstanceExistsT,\IfInstanceExistsF}
  \begin{syntax}
    \V\IfInstanceExistsTF \marg{type} \marg{instance} \marg{true code} \marg{false code}
  \end{syntax}
判断实例是否存在。
\end{function}


\section{\cusmodule{ltx}模块}

\nofuncskip
\begin{function}[EXP]{\cus@filename}
  \begin{syntax}
    \V*|\cus@filename| \marg{filename}
  \end{syntax}
自动处理 \meta{filename} 中的特殊符号（活动字符和双引号等），展开为一个文件名。

目录（文件夹）的分隔符必须为 \texttt/，即使是在 Windows 系统上。
\end{function}

\begin{function}{\cus@getgraphicsname}
  \begin{syntax}
    \V*|\cus@getgraphicsname| \meta{cmd} \marg{filename}
  \end{syntax}
将 \meta{cmd} 设置为 \meta{filename} 对应的图片文件名，若图片不存在则为 \tn{relax}。

它会自动查找 \tn{setgraphicspath} 设置的路径，且可以自动补全文件扩展名。

需要用户自行加载 \pkg{graphicx} 宏包。
\end{function}

\begin{function}[noTF]{\cus_get_graphics_full_name:nN,}
  \begin{syntax}
    \V*|\cus_get_graphics_full_name:nN|   \marg{filename} \meta{tl}
    \V*|\cus_get_graphics_full_name:nNTF| \marg{filename} \meta{tl} \marg{true code} \marg{false code}
  \end{syntax}
将 \meta{tl} 设置为 \meta{filename} 对应的图片文件名，若图片不存在则为 \cs{q_no_value}。

它会自动查找 \tn{setgraphicspath} 设置的路径，且可以自动补全文件扩展名。

需要用户自行加载 \pkg{graphicx} 宏包。
\end{function}


\section{\cusmodule{util}模块}\label{sec:module-util}

\subsection{交叉引用、超链接和书签}

\LaTeX 的 \tn{label} 可以标记位置用于交叉引用，\pkg{hyperref} 宏包还提供了超链接的功能，\pkg{bookmark} 宏包则提供了书签功能。本模块封装了其中的某些命令，但不会自动加载这些宏包
（如果没有加载所需的宏包，这些命令不会报错，只是被忽略掉了），
需要用户自行加载或使用 \CusTeX 提供的宏包加载机制来加载。

\begin{function}[EXP]{\labelinfo,\cus_label_info:nn}
  \begin{syntax}
    \verb|\labelinfo| \marg{info} \marg{label}
  \end{syntax}
获取 \meta{label} 的相关信息。\meta{label} 可以为空，代表最近写入的那个 label。

可获得的信息 \meta{info} 为：
\begin{itemize}[nosep]
  \item \texttt{name}，\meta{label} 的值，若 \meta{label} 不存在则为 \cs{c_novalue_tl} 的值；
  \item \texttt{page}，获得 \meta{label} 所在页的 \tn{thepage} 值，若 \meta{label} 不存在则为 \texttt{0}；
  \item \texttt{ref}，获得 \meta{label} 中的第一个数据项，也就是 \tn{ref}\marg{label} 显示的内容，若 \meta{label} 不存在则为 \cs{c_novalue_tl} 的值，可使用 \cs{IfNoValueTF} 或 \cs{tl_if_novalue:nTF} 判断；
  \item \texttt{anchor}，获得链接到 \meta{label} 所在位置的锚点，若 \meta{label} 不存在或未加载 \pkg{hyperref} 则为 \texttt{Doc-Start}；
  \item \texttt{pageanchor}，获得链接到 \meta{label} 所在页的锚点，若 \meta{label} 不存在或未加载 \pkg{hyperref} 则为 \texttt{Doc-Start}。
\end{itemize}

注意：\texttt{anchor} 和 \texttt{pageanchor} 不会将 \pkg{hyperref} 的 
\tn{HyperDestNameFilter} 命令考虑在内，
如果需要，可以使用 \pkg{hyperref} 的 \tn{hyperget}\texttt{\{anchor\}}\marg{label} 和 
\tn{hyperget}\texttt{\{pageanchor\}}\marg{label}。
\end{function}

\begin{function}{\cus_newlabel_now:nnnnnn,\cus_newlabel_now:xxxxxx,
  \cus_newlabel_shipout:nnnnnn,\cus_newlabel_shipout:xxxxxx,
  \cus_newlabel_shipout_x:nnnnnn,\cus_newlabel_shipout_x:xxxxxx}
  \begin{syntax}
    \V*|\cus_new_label_now:nnnnnn| \marg{label} \marg{ref data} \marg{thepage} 
    ~~~~\marg{current label name} \marg{anchor} \marg{extra}
  \end{syntax}
写入一个 \meta{label}。

\meta{label}、\meta{thepage}、\meta{current label name}、\meta{anchor} 总是立即展开。

它们\emph{不会}执行 \hook{label} 钩子。

作为一个例子，\verb|\label{#}| 类似于 
\verb|\cus_newlabel_shipout:nnnnnn| \verb|{#}|
\verb|{\@currentlabel}| \verb|{\thepage}| \verb|{\@currentlabelname}|
\verb|{\@currentHref}| \verb|{\@kernel@reserved@label@data}|。
\end{function}

\begin{function}{\cus_make_target:n,\cus_make_unique_target:n}
  \begin{syntax}
    \V*|\cus_make_target:n| \marg{target}
    \V*|\cus_make_unique_target:n| \marg{target}
  \end{syntax}
\cs{cus_make_target:n} 以 \meta{target} 为名，创建一个锚点。\meta{target} 必须唯一。锚点位置自动升高 \tn{normalbaselineskip}。

\cs{cus_make_unique_target:n} 创建一个锚点，其名以 \meta{target} 为前缀，由一个共享的计数器保证这个锚点名唯一，每调用一次，这个计数器都会自增。

加载了 \pkg{hyperref} 宏包才有效。
\end{function}

\begin{function}{\g_cus_anchor_tl,\cus_gset_next_anchor_name:n,\cus_gset_next_anchor_raise:n}
  \begin{syntax}
    \V*|\cus_gset_next_anchor_name:n|  \marg{name}
    \V*|\cus_gset_next_anchor_raise:n| \marg{dim}
  \end{syntax}
\cs{g_cus_anchor_tl} 保存了最近一个锚点的名称，它是只读的。相当于 \tn{@cur\-rentHref}。

\cs{cus_gset_next_anchor_name:n} 设置下一个锚点的名称。\meta{name} 被立刻展开。

\cs{cus_gset_next_anchor_raise:n} 设置下一个锚点要升高的高度。\meta{dim} 立即被计算。
直接使用的 \tn{Hy@raisedlink} 不会受影响。% 它的高度保存在 \HyperRaiseLinkDefault 宏里。

它们也会影响 \pkg{hyperref} 宏包中其它创建锚点的命令。
\end{function}


\begin{function}{\cus_ref_label:nn,\cus_ref_target:nn,\cus_ref_label_box:nn,\cus_ref_target_box:nn}
  \begin{syntax}
    \V*|\cus_ref_label:nn|  \marg{label} \marg{text}
    \V*|\cus_ref_target:nn| \marg{target} \marg{text}
    \V*|\cus_ref_label_box:nn|  \marg{label} <material>
    \V*|\cus_ref_target_box:nn| \marg{target} <material>
  \end{syntax}
将 \meta{text} 或 \meta{material} 链接到 \meta{label} 或 \meta{target}。

\meta{text} 可以断行，但不能包含特殊文本。\meta{material} 中可以包含特殊文本，如 verbatim，仅能在特殊的位置断行。
另见 \cs{cus_ref_label_shbox:nn}、\cs{cus_ref_target_shbox:nn}。

\meta{material} 可以是 \texttt\{\BNFN{horizontal mode material}\texttt\}，正如 \tn{hbox}
\texttt\{\BNFN{horizontal mode material}\texttt\} 那样，也可以有 \BNFN{box specification}。左右括号可以是隐式的。

加载了 \pkg{hyperref} 宏包才有效。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cus_ref_label_box:nn { sec:module-util } \bgroup\verb|本节开始|\egroup 或
\cus_ref_label:nn { sec:module-util } { 链接到本节开始，但是是很长很长很长很长很长很长的可以断行的文字。 }
\ExplSyntaxOff
\stopxamplecode
\xampleprint 
\end{xample}

\begin{function}{\cus@colorfile,\cus@colorlink,\cus@colorcite,
  \cus@colorurl,\cus@colorrun,\cus@colormenu}
把文字的颜色改为对应的颜色。

注意：使用 \verb|\hypersetup{linkcolor=red}| 等修改的颜色仅在 PDF 阅读器中显示，在打印时不会显示，而使用上述 6 个命令以及 \tn{color}、\cs{color_select:n} 等修改的颜色会在打印时显示。
\end{function}

\begin{function}{\cus_bookmark:nn,\cus_gset_next_bookmark_text:n}
  \begin{syntax}
    \V*|\cus_bookmark:nn| \marg{options} \marg{bookmark}
    \V*|\cus_gset_next_bookmark_text:n| \marg{bookmark}
  \end{syntax}
设置书签。或设置下一个书签的文字。

\cs{cus_bookmark:nn} 是对 \tn{bookmark} 的封装。加载了 \pkg{bookmark} 宏包才有效。
\end{function}

\begin{function}[pTF]{\cus_if_pdfstring:}
  \begin{syntax}
    \V*|\cus_if_pdfstring:TF| \marg{true} \marg{false}
  \end{syntax}
判断是否在书签等 PDF 字段内。
\end{function}

\begin{function}{\cus_hyper_anchor:n,\cus_hyper_anchor_start:n,\cus_hyper_anchor_stop:,
  \cus_hyper_link:nnn,\cus_hyper_link_start:nn,\cus_hyper_link_stop:,
  \cus_hyper_link_file:nnn,\cus_hyper_link_url:nn,
  \cus_hyper_link_launch:nnn,\cus_hyper_link_name:nn}
  \begin{syntax}
    \V*|\cus_hyper_anchor:n|       \marg{destination name}
    \V*|\cus_hyper_anchor_start:n| \marg{destination name} \meta{content}
    ~~~~\V*|\cus_hyper_anchor_stop:|
    \V*|\cus_hyper_link:nnn|      \marg{context} \marg{destination name} \marg{link text}
    \V*|\cus_hyper_link_start:nn| \marg{context} \marg{destination name} \meta{content}
    ~~~~\V*|\cus_hyper_link_stop:|
    \V*|\cus_hyper_link_file:nnn| \marg{link text} \marg{filename} \marg{destname}
    \V*|\cus_hyper_link_url:nn|   \marg{link text} \marg{url}
    \V*|\cus_hyper_link_launch:nnn| \marg{filename} \marg{link text} \marg{Parameters}
    \V*|\cus_hyper_link_name:nn|    \marg{action} \marg{link text}
  \end{syntax}
对驱动文件提供的基础命令的封装，必须加载 \pkg{hyperref} 宏包才有效。其中最后两个仅在使用了通用驱动（generic driver）才有效（即使用了 \cs{DocumentMetadata} 的特性）。

它们仅创建锚点（或创建超链接），不会设置任何格式。
\end{function}

\begin{function}{\cus_gset_next_page_label:n,
  \cus_gset_page_label:n,\cus_gset_page_label_code:n,\cus_reset_page_label_code:}
  \begin{syntax}
    \V*|\cus_gset_next_page_label:n| \marg{page label}
    \V*|\cus_gset_page_label:n|      \marg{page label}
    \V*|\cus_gset_page_label_code:n| \marg{code}
    \V*|\cus_reset_page_label_code:|
  \end{syntax}
这些命令用于设置在阅读器中显示的页码。\meta{page label} 一般包含 \tn{thepage} 或
\verb|\arabic{page}| 等内容。\cs{cus_gset_next_page_label:n} 相当于 \pkg{hyperref}
宏包的 \tn{thispdfpagelabel} 命令，用于设置\emph{本页}的 page label。

\meta{code} 带有一个参数，使用 \tn{renewcommand}、\tn{def}、\cs{tl_set:Nn}
等命令设置这个参数方可改变 page label。注意，\meta{code} 执行于 \hook{shipout/before}
钩子中，此时 \texttt{page} 计数器已经递增，但 \cs{g_shipout_readonly_int}
（\tn{ReadonlyShipout\-Coun\-ter}）、\cs{g_shipout_totalpages_int} 还未改变。

加载了 \pkg{hyperref} 宏包且 \opt{pdfpagelabels} 为真才有效。
\end{function}

例如，下例为阅读器中显示的\emph{本页}页码加上 \text{SP.} 前缀。
\begin{xample}
\ExplSyntaxOn
\cus_gset_next_page_label:n { SP.\thepage }
% 相当于下面这段代码
% \cus_gset_page_label_code:n 
%   {
%     \tl_set:Nn #1 { SP.\thepage }
%     \cus_reset_page_label_code: 
%   }
\ExplSyntaxOff
\stopxamplecode
\xamplecode\xampletext\medskip
\end{xample}

\subsection{向前查找和收集内容}

\LaTeXiii 的以 \texttt{peek} 为模块名的命令可以用于向前查找、检测和分析记号，\pkg{collectbox} 宏包提供了向前收集内容存进盒子的功能。本模块也实现了类似的命令。

\begin{function}{\cus_peek_verbatim:nw}
  \begin{syntax}
    \V*|\cus_peek_verbatim:nw| \marg{tokens_1} \marg{balanced tokens}
    \V*|\cus_peek_verbatim:nw| \marg{tokens_1} \meta{token} \meta{tokens} \meta{token}
  \end{syntax}
以纯文字的形式向后收集一段代码，然后把它们放在一个组中，然后把 \meta{tokens} 放到它前面。
\meta{balanced tokens} 或 两个相同 \meta{token} 之间的 \meta{tokens} 不能作为命令的参数。
\end{function}

\begin{function}{\cus_peek_act:nnnnn}
  \begin{syntax}
    \V*|\cus_peek_act:nnnnn| 
    ~~~~\marg{normal} \marg{space} \marg{group begin} \marg{group end} \marg{else}
  \end{syntax}
类似于 \cs{peek_N_type:TF}，但对后面的记号执行对应的分支。这个记号仍然保留下来。

\meta{else} 的情况一般是后面的记号为 \tn{outer} 宏。
\end{function}

\subsection{分析记号}

\meta{token list} 可能含有特定的模式，本模块提供了分析某些特定模式的命令。

\begin{function}[pTF]{\cus_if_head_int:n}
  \begin{syntax}
    \verb|\cus_if_head_int:nTF| \marg{tl} \marg{true code} \marg{false code}
  \end{syntax}
测试 \meta{tl} 是否以数字起始。
\end{function}

\begin{xample}
\ExplSyntaxOn
  1.  \cus_if_head_int:nTF { 2022  } { T } { F }
\ 2.  \cus_if_head_int:nTF { +2022 } { T } { F } % 正数
\ 3.  \cus_if_head_int:nTF { -2022 } { T } { F } % 负数
\ 4.  \cus_if_head_int:nTF { '3746 } { T } { F } % 8进制数
\ 5.  \cus_if_head_int:nTF { "7E6  } { T } { F } % 16进制数
\ 6.  \cus_if_head_int:nTF { Notnu } { T } { F }
\ 7.  \cus_if_head_int:nTF { \par  } { T } { F }
\ 8.  \cus_if_head_int:nTF { \c_true_bool } { T } { F } % \char
\ 9.  \cus_if_head_int:nTF { \c_one_int   } { T } { F } % int (count)
\ 10. \cus_if_head_int:nTF { \tracingmacros } { T } { F }
\ 11. \cus_if_head_int:nTF { \the\tracingmacros } { T } { F } % 展开为整数
\ 12. \cus_if_head_int:nTF { \baselineskip } { T } { F }
\ 13. \cus_if_head_int:nTF { \number\baselineskip } { T } { F } % 展开为整数
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\cus_arg_to_keyval_apply:nnN,\cus_arg_to_keyval_apply:nnn}
  \begin{syntax}
    \V*|\cus_arg_to_keyval_apply:nnN| \marg{key name} \marg{arg} \meta{function}
    \V*|\cus_arg_to_keyval_apply:nnn| \marg{key name} \marg{arg} \marg{tokens}
  \end{syntax}
判断 \meta{arg} 是否为键值对；如果不是则把 \meta{key name} 作为键名，\meta{arg} 作为值，
形成一个键值对。再把这键值放在 \meta{function}（或 \meta{tokens}） 的后面。
\end{function}

\begin{function}{\cus_if_keyval_apply:nNN,\cus_if_keyval_apply:nnn}
  \begin{syntax}
    \V*|\cus_if_keyval_apply:nNN| \marg{arg} \meta{true function} \meta{false function}
    \V*|\cus_if_keyval_apply:nnn| \marg{arg} \marg{true tokens} \marg{false tokens}
  \end{syntax}
首先检查 \meta{arg} 是否为键值对，如果是，则使用 \meta{true} 分支，
否则使用 \meta{false} 分支。再把 \meta{arg} 处理后的结果
放在 \meta{function}（或 \meta{tokens}） 的后面。
\end{function}

\begin{function}{\cus_if_keyval_variable:nNnn}
  \begin{syntax}
    \V*|\cus_if_keyval_variable:nNnn| \marg{arg} \meta{variable} \marg{true code} \marg{false code}
  \end{syntax}
把处理后的 \meta{arg} 保存到 \meta{variable} 中，\meta{code} 可以使用这个 \meta{variable}。
\end{function}

判断是否为键值对的方式和 \pkg{ltcmd} 的 \texttt{=} spec 一样，详见 \file{usrguide.pdf}。

\begin{xample}
\ExplSyntaxOn
\cs_new_protected:Npn \fiicmd #1
  {
    \cus_if_keyval_variable:nNnn {#1} \l_tmpa_tl
      { Y [ \tl_to_str:N \l_tmpa_tl ] }
      { N ( \tl_to_str:N \l_tmpa_tl ) }
  }
\ExplSyntaxOff
\ttfamily
\fiicmd{,d=l}
\fiicmd{=,d=l}
\fiicmd{=l}
\fiicmd{$=l$}
\fiicmd{{=,d=l}}
\fiicmd{}
\fiicmd{ }
\fiicmd{=,}
\fiicmd{a{=},}
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\cus_split_bracket_head_default:nn,\cus_split_bracket_head:n}
\begin{syntax}
  \verb|\cus_split_bracket_head_default:nn| \marg{default} \marg{tl}
  \verb|\cus_split_bracket_head:n|                   \;\marg{tl}
\end{syntax}
解析 \meta{tl}。判断其是否以一对方括号（\verb|[ ]|）起始，若是则将方括号后的内容放入一个集合中
（\verb|{ }|）。方括号不可直接嵌套，需放入组中。
\end{function}

\begin{xample}
\ttfamily \ExplSyntaxOn
\exp_args:Ne \tl_to_str:n
  {
           1. \cus_split_bracket_head:n { }
    \space 2. \cus_split_bracket_head:n { tail }
    \space 3. \cus_split_bracket_head:n { [bracket]tail }
    \space 4. \cus_split_bracket_head:n { [bracket] }
  }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\cus_split_bracket_tail_default:nn,\cus_split_bracket_tail:n}
\begin{syntax}
  \verb|\cus_split_bracket_tail_default:nn| \marg{tl} \marg{default}
  \verb|\cus_split_bracket_tail:n|          \marg{tl}
\end{syntax}
解析 \meta{tl}。判断其是否以一对方括号（\verb|[ ]|）结尾，若是则将方括号前的内容放入一个集合中
（\verb|{ }|）。方括号不可直接嵌套，需放入组中。
\end{function}

\begin{xample}
\ttfamily \ExplSyntaxOn
\exp_args:Ne \tl_to_str:n
  {
           1. \cus_split_bracket_tail:n { }
    \space 2. \cus_split_bracket_tail:n { head }
    \space 3. \cus_split_bracket_tail:n { head[bracket] }
    \space 4. \cus_split_bracket_tail:n { [bracket] }
  }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\parserange:nnnN,\parserange:nnvN,\parserange:nneN,
  \parserange:nnN,\parserange:nvN,\parserange:neN}
  \begin{syntax}
    \verb|\parserange:nnnN| \marg{最小值} \marg{最大值} \marg{range clist} \meta{sequence}
    \verb|\parserange:nnN|  \marg{最大值} \marg{range clist} \meta{sequence}
  \end{syntax}
解析一个整数列表，将其保存至 \meta{sequence} 中。
可使用 \verb|->| 标记连续的范围。若范围的开始为空，则设它为 \meta{最小值}，若终止为空，则
设它为 \meta{最大值}。

逆序的范围无效。

如果 \meta{最小值} 被省略，则设它为 1。
\end{function}

\begin{function}{\parserange_check:,\parserange_nocheck:}
是否检查越界值。

当设置了检查越界值时，结果的项被限制在 \meta{最小值} 和 \meta{最大值} 之中（含边界）。

否则，忽略这一限制，但逆序的范围仍然无效。
\end{function}

\begin{xample}
\ExplSyntaxOn
\parserange:nnN { 10 } { ->2, 4->7, 8-> } \l_tmpa_seq
\seq_use:Nn \l_tmpa_seq { ,~ } \par 

\parserange:nnN { 10 } { -1->2, 4->7, 9->12, 20, 30->32 } \l_tmpa_seq
\seq_use:Nn \l_tmpa_seq { ,~ } \par 

\parserange_nocheck:
% 不检查越界
\parserange:nnN { 10 } { -1->2, 9->12, 20, 30->32 } \l_tmpa_seq
\seq_use:Nn \l_tmpa_seq { ,~ } \par 

\parserange:nnN { 10 } { -1->2, 9->12, 20, 32->30 } \l_tmpa_seq
\seq_use:Nn \l_tmpa_seq { ,~ } \par % 逆序，无效
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\parserange_set_to_int:n}
  \begin{syntax}
    \V*|\parserange_set_to_int:n| \marg{code}
  \end{syntax}
有时，给出的值并非为整数，但可以通过某种方式转为整数。
此时可以使用此函数设置转化为整数的方法。\meta{code} 可以使用一个参数，
为原始的值。\meta{code} 必须为 \texttt f-expandable。
\end{function}

\begin{function}{\parserange_use_delimiter:n,\parserange_use_default_delimiter:}
  \begin{syntax}
    \verb|\parserange_use_delimiter:n| \marg{delimiter}
    \verb|\parserange_use_default_delimiter:| 
  \end{syntax}
设置范围的分隔符为 \meta{delimiter}，默认为 \verb|->|。对分隔符的修改应该是局部的。
\end{function}

\begin{xample}
\ExplSyntaxOn
\parserange_use_delimiter:n { - }
\parserange:nnN { 10 } { 0-2, 7-12, 20, 32-30 } \l_tmpa_seq
\seq_use:Nn \l_tmpa_seq { ,~ } 
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\cus_tl_split_braced:nn}
  \begin{syntax}
    \V*|\cus_tl_split_braced:nn|   \marg{tl_1} \marg{tl_2}
  \end{syntax}
\cs{cus_tl_split_braced:nn} 提取 \meta{tl_1} 和 \meta{tl_2} 的值，将它们分别拆成两个部分，
这拆分的四个部分中，第 1、3 部分的长度为 \meta{tl_1} 和 \meta{tl_2} 中长度的较小值。
第 2、4 个部分为剩余的值，当中至少有一个为空。这四个部分的每一项都由括号括起来。
即，若设 \meta{tl_1} 的长度小于 \meta{tl_2}，则 
\begingroup \linespread{1}\selectfont \jot=0pt
\begin{align*}
  \text{\veta{tl_1}} &= a_1,a_2,\cdots,a_m \\
  \text{\veta{tl_2}} &= b_1,b_2,\cdots,b_m,b_{m+1},\cdots,b_n \\
  \text{\veta{res_1}} &= \text{\veta{tl_1}} = a_1,a_2,\cdots,a_m \\
  \text{\veta{res_2}} &= \text{\veta{empty}} \\ 
  \text{\veta{res_3}} &= b_1,b_2,\cdots,b_m \\
  \text{\veta{res_4}} &= b_{m+1},\cdots,b_n
\end{align*}
\endgroup
\UNEXPANDEDRESULT
\end{function}

\begin{function}{\cus_tl_split_braced:NNNN}
  \begin{syntax}
    \V*|\cus_tl_split_braced:NNNN| <tl var_1> <tl var_2> <tl var_3> <tl var_3>
  \end{syntax}
\cs{cus_tl_split_braced:NNNN} 提取 \meta{tl var_1} 和 \meta{tl var_2} 的值，
将它们分别拆成两个部分，将这些值保存到 4 个 \meta{tl var} 中。
其中 \meta{tl var_1} 和 \meta{tl var_2} 为前一部分，\meta{tl var_3} 和 \meta{tl var_4}
为后一部分，当中至少有一个为空。
\end{function}

\begin{xample}
\ttfamily \ExplSyntaxOn
\tl_to_str:e { \cus_tl_split_braced:nn {12345} {ABCDEFG} } \par 
\tl_set:Nn \l_tmpa_tl { 12345   }
\tl_set:Nn \l_tmpb_tl { ABCDEFG }
\cus_tl_split_braced:NNNN \l_tmpa_tl \l_tmpb_tl \l_tmpc_tl \l_tmpd_tl
[ \tl_to_str:N \l_tmpa_tl ] \par 
[ \tl_to_str:N \l_tmpb_tl ] \par 
[ \tl_to_str:N \l_tmpc_tl ] \par 
[ \tl_to_str:N \l_tmpd_tl ] \par 
\ExplSyntaxOff
\stopxamplecode
\xampleprint 
\end{xample}

\subsection{杂项}

\nofuncskip 
\begin{function}[pTF]{\cus_if_preamble:,\cus_if_document:,
  \cus_if_class_loaded:,\cus_if_after_documentclass:}
  \begin{syntax}
    \verb|\cus_if_preamble:TF| \marg{true code} \marg{false code}
  \end{syntax}
测试是否在导言区、正文，或是否已经加载了文档类，或是否在 \tn{documentclass} 后。
\end{function}

\begin{function}[EXP]{\cus_exp_args:Nd,\cus_exp_args:NNd,\cus_exp_args:Nnd,
  \cus_exp_last_unbraced:Nd,\cus_exp_last_unbraced:NNd,\cus_exp_last_unbraced:Nnd}
  \begin{syntax}
    \verb|\cus_exp_args:Nd| \meta{function} \marg{tokens}
    \verb|\cus_exp_args:NNd| \meta{function} \meta{function} \marg{tokens}
    \verb|\cus_exp_args:Nnd| \meta{function} \marg{tokens_1} \marg{tokens_2}
  \end{syntax}
展开 \meta{tokens} 或 \meta{tokens_2} 两次。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cus_exp_args:Nd \tl_to_str:n
  { \prg_replicate:nn { 5 } { \CusTeX } }
\ExplSyntaxOff
\stopxamplecode
\xampleprint 
\end{xample}

\begin{xample}
\ExplSyntaxOn
\cus_exp_last_unbraced:Nd \tl_to_str:n 
  { \char_generate:nn { `\{ } { 1 } } \token_to_str:N }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\cus_exp_args:nw,\cus_exp_args:ew}
  \begin{syntax}
    \V*|\cus_exp_args:nw| \marg{spec} \meta{tl}
  \end{syntax}
按 \meta{spec} 将 \meta{tl} 依次展开。\meta{spec} 的长度不限。
为 \cs[no-index]{exp_args:..} 的增强版，但若 \cs[no-index]{exp_args:\meta{spec}}
存在，则不宜使用 \cs{cus_exp_args:nw}。

\meta{spec} 为下列之组合：
\texttt{N}、\texttt{c}、\texttt{p}、
\texttt{n}、\texttt{n_un}、
\texttt{o}、\texttt{o_un}、
\texttt{d}、\texttt{d_un}、
\texttt{f}、\texttt{f_un}、
\texttt{V}、\texttt{V_un}、
\texttt{v}、\texttt{v_un}、
\texttt{e}、\texttt{e_un}、
\texttt{_un}、\texttt{\textvisiblespace _un}
以及由括号括起来的空或空格。忽略未被保护的空格。

\veta{空}的作用是把该项的内容清除，\veta{空格}的作用是添加一个空格。
带有 \texttt{_un} 后缀的，使用时需有括号括起来，其结果不自动添加 \verb|{}|。

两次展开即可得到结果。
\end{function}

\begin{xample}
\ExplSyntaxOn
\tl_set:Nn \l_tmpa_tl { \relax }
\cus_exp_args:NNd \tl_set:Nn \l_tmpb_tl
  { 
    \cus_exp_args:nw { c v {~} o {o_un} f { } {e_un} } 
      { l_tmpa_tl } { l_tmpa_tl } { \l_tmpa_tl } { \l_tmpa_tl }
      { ~ 01 } {ig} { \prg_replicate:nn { 5 } { 0 } } 
  }
\ttfamily \tl_to_str:N \l_tmpb_tl
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

注意，\texttt{o_un}、\texttt{V_un} 和 \texttt{v_un} 的行为与
\cs[no-index]{exp_last_unbraced:N..[o|V|v]} 的行为并不一致，
假设有 \verb|\def\foo#1{#1}|，\verb|\exp_last_unbraced:No \~ \foo {a}|
可以得到 \~a， \verb|\cus_exp_args:nw {N{o_un}} \~ \foo {a} | 则会出错。

\begin{function}[EXP]{\cus_serial_exp_args:nnw,\cus_serial_exp_args:eew}
  \begin{syntax}
    \V*|\cus_serial_exp_arg:nnw| \marg{spec_{1m}} \marg{spec_{2}} \meta{tl_{1m}} \marg{tl}
  \end{syntax}
依次执行 
\cs{cus_exp_args:nw} \texttt\{\meta{spec_{1m}}\meta{spec_{2i}}\texttt\} \meta{tl_i} 
（$1\leqslant i\leqslant \veta{\meta{spec_{2}}的长度}$），
其中 \meta{spec_{2i}} 为 \meta{spec_2} 的第 $i$ 项，
\meta{tl_i}（$i\geqslant 2$） 为前一次的展开结果，
\meta{tl_1} 为 \meta{tl_{1m}}\marg{tl}。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cs_set_protected:Npn \foo #1 { \int_eval:n { 1+2+#1 } }
\tl_set:Nn \l_tmpa_tl { \l_tmpb_tl }
\tl_set:Nn \l_tmpb_tl { \foo }
\cus_serial_exp_arg:nnw { N } { o o f } \tl_to_str:n { \l_tmpa_tl { 3 } }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\ExpandArgs}
  \begin{syntax}
    \V\ExpandArgs \marg{spec} \meta{token} \meta{tl}
  \end{syntax}
按 \meta{spec} 将 \meta{tl} 依次展开。这是由 \LaTeXe 提供的命令。本模块进一步增强了它。
%
当 \cs[no-index]{exp_args:N\meta{spec}} 存在时，使用它，否则使用 
\cs{cus_exp_args:nw} \texttt\{\texttt N\meta{spec}\texttt\}。
\end{function}

\begin{function}[EXP]{\cus_use_none_num:nw}
  \begin{syntax}
    \V*|\cus_use_none_num:nw| \marg{num} \meta{tl}
  \end{syntax}
移除 \meta{tl} 的前 \meta{num} 项。\meta{tl} 必须至少有 \meta{num} 项。忽略未被保护的空格。
如果 \meta{num} 小于等于 0，则什么也不做。

两次展开即可得到结果。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cus_exp_args:NNd \tl_set:Nn \l_tmpa_tl 
  { \cus_use_none_num:nw { 5 } 1234567890 }
\tl_to_str:N \l_tmpa_tl 
\ExplSyntaxOff
\stopxamplecode
\xampleprint 
\end{xample}

\begin{function}[EXP]{\cus_exp_num:nN,\cus_exp_after_num:nwN}
  \begin{syntax}
    \V*|\cus_exp_num:nN| \marg{num} \meta{token_1}
    \V*|\cus_exp_after_num:nwN| \marg{num} \meta{token_0} \meta{token_1}
  \end{syntax}
展开 \meta{token_1} \meta{num} 次。如果 \meta{num} 小于等于 0，则什么也不做。

两次展开即可得到结果。
\end{function}

\begin{function}[EXP]{\cus_tl_use:Nnnn,\cus_tl_use:Nn}
  \begin{syntax}
    \V*|\cus_tl_use:Nnnn| <tl var> <{separator between two}> 
    ~~~~<{separator between more than two}> <{separator between final two}>
    \V*|\cus_tl_use:Nn|   <tl var> <{separator}>
  \end{syntax}
把 \meta{tl var} 放在输出流中，每项之间加上正确的 \meta{separator}，类似于 
\cs{clist_use:Nnnn} 和 \cs{clist_use:Nn}。

忽略未使用 \verb|{}| 保护的空格。

\begin{texnote}
\UNEXPANDEDRESULT
\end{texnote}
\end{function}

\begin{function}[EXP]{\cus_tl_use:nnnn,\cus_tl_use:nn}
  \begin{syntax}
    \V*|\cus_tl_use:nnnn| <{tl}> <{separator between two}> 
    ~~~~<{separator between more than two}> <{separator between final two}>
    \V*|\cus_tl_use:nn|   <{tl}> <{separator}>
  \end{syntax}
把 \meta{tl} 放在输出流中，每项之间加上正确的 \meta{separator}，类似于 
\cs{clist_use:nnnn} 和 \cs{clist_use:nn}。

忽略未使用 \verb|{}| 保护的空格。

\begin{texnote}
\UNEXPANDEDRESULT
\end{texnote}
\end{function}

\begin{xample}
\ExplSyntaxOn
\tl_set:Nn \l_tmpa_tl { ~{xparse}{command}~{is}{~}{not}~{expandable}! }
\tl_set:Nx \l_tmpb_tl { \cus_tl_use:Nnnn \l_tmpa_tl { - } { - } { -and- } }
\ttfamily \tl_to_str:N \l_tmpb_tl
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\cus_act_case:nnnn,
  \cus_act_case_true:nnnn,\cus_act_case_false:nnnn}
  \begin{syntax}
    \V*|\cus_act_case_true:nnnn| \marg{tl} \marg{processor list} \marg{fallback tokens} \marg{act args}
  \end{syntax}
\meta{processor list} 中奇数项为判断函数，偶数项为对应的处理代码，如果判断为真（或为假），
则使用对应的处理代码，如果 \meta{processor list} 中的判断函数都判断为假（或都为真），则使用 
\meta{fallback tokens}。偶数项的处理代码可以使用 \meta{act args} 作为参数。

\cs{cus_act_case:nnnn} 是 \cs{cus_act_case_true:nnnn} 的另一个名字。
\end{function}

\begin{xample}
\ExplSyntaxOn
% 判断 : (catcode=12) 和 : (catcode=11) 在哪种情况下相等
\exp_args:No \cus_act_case_true:nnnn 
  { \token_to_str:N : } % \catcode`\:=12
  { 
    { \tl_if_eq:nnTF  { : }       } { tl  ~ \use:n } % \catcode`\:=11 不相等
    { \token_if_eq_catcode:NNTF : } { cat ~ \use:n } % \catcode`\:=11 不相等
    { \str_if_eq:nnTF { : }       } { str ~ \use:n } % string 相等
  }
  { not ~ equal \use_none:n }
  { {eq} }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[EXP]{\cus_if_lazy_all:nnTF,\cus_if_lazy_any:nnTF}
  \begin{syntax}
    \V*|\cus_if_lazy_all:nnTF| <{tl}> <{test list}> <{true}> <{false}>
  \end{syntax}
判断 \meta{tl} 是否满足 \meta{test list} 中的所有（或任一）判断，如果是则使用 \meta{true}，否则使用 \meta{false}。
\end{function}

\begin{xample}
\ExplSyntaxOn
\exp_args:No \cus_if_lazy_any:nnTF { \token_to_str:N : }
  {
    { \tl_if_eq:nnTF  {:} } % 不满足
    { \str_if_eq:nnTF {:} } % 满足
  }
  { true }
  { false }
\ 
\exp_args:No \cus_if_lazy_all:nnTF { \token_to_str:N : }
  {
    { \tl_if_single_token:nTF      } % 满足
    { \token_if_eq_charcode:NNTF : } % 满足
    { \token_if_eq_catcode:NNTF  : } % 不满足
  }
  { true }
  { false }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

这两个命令主要用于那些不可展开的测试，如 \cs{tl_if_eq:nnTF}、\cs{regex_match:nnTF}，
或者测试方式多样，如既要使用 \cs{token_if_eq_meaning:NNTF} 又要使用 
\cs{token_if_eq_charcode:NNTF}，因此不能直接使用 \cs{token_case_mean\-ing:\-NnTF}
和 \cs{token_case_charcode:NnTF}。

\begin{function}{\cus_map_nest_code:Nnnn,\cus_map_nest_variable:NnnNn}
  \begin{syntax}
    \V*|\cus_map_nest_code:Nnnn|      \meta{map tokens function} \marg{arg} \marg{nest} \marg{code}
    \V*|\cus_map_nest_variable:NnnNn| \meta{map tokens function} \marg{arg} \marg{nest} \meta{variable} \marg{code}
  \end{syntax}
使用 \meta{map tokens function} 迭代 \meta{arg}，嵌套 \meta{nest} 次。
\meta{map tokens function} 为以 \verb|_map_tokens:..| 结尾的宏，如 
\cs{tl_map_tokens:nn}，\cs{seq_map_tokens:Nn}。根据第一个参数的不同，\meta{arg} 也要随之改变。

\meta{code} 可以使用嵌套的结果，嵌套结果的长度为 \meta{nest}，\meta{code} 的执行次数为 
\underline{第一层 \meta{code} 执行次数的 \meta{nest} 次幂}。
\end{function}

\begin{xample}
\ExplSyntaxOn 
\cs_set:Npn \__this_box:n #1
  {
    \hbox_set:Nn \l_tmpa_box { \scriptsize #1 }
    \hbox_to_wd:nn { \box_wd:N \l_tmpa_box }
      { % 上面显示十进制数字，下面显示二进制数字
        \oalign { 
          \hfil \int_from_bin:n {#1} \hfil \cr 
          \box_use_drop:N \l_tmpa_box 
        } 
      }
  }
\cus_map_nest_code:Nnnn \tl_map_tokens:nn { 01 } { 4 }
  { [ \__this_box:n {#1} ] } % 共执行 (len("01"))^4=16 次
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

% \subsection{\cusmodule{psr}，处理器}

% 有时，一个命令需要根据不同的设置显示不同的效果，但这个命令的执行逻辑已经确定，重新修改这个命令
% 不是一个好的做法，因为无法保证对它的修改是正确的。一般可以通过在这些命令中插入钩子（hook）或
% 修改这个命令内部真正有效的命令来间接地修改它。后者就是\emph{处理器}的主要思想。

% 处理器是包装过的宏。

% 一个\emph{处理器}是在某些命令内部发挥作用的接口宏，处理器的\emph{规则}是处理器真正发挥执行
% 操作的宏，通过让处理器\emph{遵循}（obey）某个规则来控制处理器以何种方式运行
% （这些规则也是宏）。这样我们只需定义
% 一些规则，然后根据需要让处理器遵循某个规则，从而可以达到不同的效果。

% 与使用条件判断相比，具有更一致的接口，可以增加任意个处理方式，并且在同一次展开时不必重复判断。

% 与直接使用宏相比，调用接口更加一致。

% \begin{function}{\cus_new_psr:nnn,\cus_set_psr:nnn,\cus_gset_psr:nnn,\cus_use_psr:n}
%   \begin{syntax}
%     \verb|\cus_new_psr:nnn| \marg{处理器} \marg{参数数目} \marg{code}
%     \verb|\cus_use_psr:n|   \marg{处理器}
%   \end{syntax}
% 创建、使用处理器。

% 每个处理器的参数数目固定。在使用处理器时，其后需有对应数目的参数。

% 处理器以其名称唯一识别，不可定义参数数目不同的同名处理器。
% \end{function}

% \begin{function}{\cus_new_psrrule:nnn,\cus_set_psrrule:nnn,\cus_gset_psrrule:nnn}
%   \begin{syntax}
%     \verb|\cus_new_psrrule:nnn| \marg{处理器} \marg{规则} \marg{code}
%   \end{syntax}
% 创建从属于 \meta{处理器} 的规则 \meta{规则}。该 \meta{规则} 可使用 \meta{处理器}
% 预先定义的参数。
% \end{function}

% \begin{function}{\cus_obey_psrrule:nn,\cus_gbey_psrrule:nn}
%   \begin{syntax}
%     \verb|\cus_obey_psrrule:nn| \marg{处理器} \marg{规则}
%   \end{syntax}
% 对 \meta{处理器} 局部或全局应用 \meta{规则}。注意，全局应用\emph{不是} \verb|gobey|。
% \end{function}

% \begin{function}[pTF]{\cus_psr_if_exist:n,\cus_psrrule_if_exist:nn,\cus_psr_if_compatible:nn}
%   \begin{syntax}
%     \verb|\cus_psr_if_exist:nTF| \marg{处理器} \marg{true code} \marg{false code}
%     \verb|\cus_psrrule_if_exist:nTF| \marg{处理器} \marg{规则} \marg{true code} \marg{false code}
%     \verb|\cus_psr_if_compatible:nnTF| \marg{处理器{}_1} \marg{处理器{}_2} \marg{true code} \marg{false code}
%   \end{syntax}
% 判断处理器、处理器的规则是否存在。或判断两个处理器是否兼容，当前，两个参数数目一致时视为兼容。
% \end{function}

% \begin{function}{\cus_exec_psrrule:nn}
%   \begin{syntax}
%     \verb|\cus_exec_psrrule:nn| \marg{处理器} \marg{规则}
%   \end{syntax}
% 直接执行 \meta{处理器} 的 \meta{规则}。可用于其它规则中，相当于一个宏。其后需有对应数目的参数。
% \end{function}

% \begin{function}[EXP]{\cus_psr_argument_count:n}
%   \begin{syntax}
%     \verb|\cus_psr_argument_count:n| \marg{处理器}
%   \end{syntax}
% 计算处理器可用的参数数目。
% \end{function}

% \begin{function}{
%   \cus_new_psrrule_eq:nnn,
%   \cus_set_psrrule_eq:nnn,
%   \cus_gset_psrrule_eq:nnn
% }
%   \begin{syntax}
%     \verb|\cus_new_psrrule_eq:nnn| \marg{处理器} \marg{规则{}_1} \marg{规则{}_2}
%   \end{syntax}
% 将 \meta{规则{}_1} 设置为与 \meta{规则{}_2} 相等，它们从属于 \marg{处理器}。
% \end{function}

% \begin{function}{
%   \cus_new_psrrule_eq_cs:nnN,
%   \cus_new_psrrule_eq_cs:nnc,
%   \cus_set_psrrule_eq_cs:nnN,
%   \cus_set_psrrule_eq_cs:nnc,
%   \cus_gset_psrrule_eq_cs:nnN,
%   \cus_gset_psrrule_eq_cs:nnc
% }
%   \begin{syntax}
%     \verb|\cus_new_psrrule_eq_cs:nnN| \marg{处理器} \marg{规则} \meta{function}
%   \end{syntax}
% 将 \meta{处理器} 的 \meta{规则} 设置为与 \meta{function} 相等。这 \meta{function}
% 的参数数必须与 \meta{处理器} 的参数数相等，且必须是非定界的变量（undelimited parameter）。
% \end{function}


\section{\cusmodule{box}模块}\label{sec:module-box-prog}

\cusmodule{box} 模块封装了一些环境或命令，用于在编程时使用。另见 \cref{sec:collectn}。

\subsection{为宽度固定和宽度可变的内容创建超链接}

\cusmodule{util} 模块提供了创建超链接的命令。
本模块则定义了可以为宽度固定和宽度可变的内容创建超链接的命令。

\begin{function}{\cus_ref_label_width:nnnn,\cus_ref_label_varwidth:nnnn,
  \cus_ref_target_width:nnnn,\cus_ref_target_varwidth:nnnn}
  \begin{syntax}
    \V*|\cus_ref_label_width:nnnn| \marg{label} \marg{vpos} \marg{width} \meta{material}
  \end{syntax}
将 \meta{material} 链接到 \meta{label} 或 \meta{target} 的位置。
其宽度（或最大宽度）为 \meta{width}，垂直位置为 \meta{vpos}。

另见 \csref{cus_ref_label_box:nn}、\csref{cus_ref_target_box:nn}。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cs_set:Npn \myparfbox 
  { 
    \collectn_minipage:Nnnnw \l_tmpa_box 
      { \fbox { \box_use_drop:N \l_tmpa_box } } 
  }
\cus_ref_label_width:nnnn { sec:module-box-prog } {b} {3cm} 
  { 链接到\par 本节开始 } |
\cus_ref_label_varwidth:nnnn { sec:module-box-prog } {t} {3cm} 
  { 链接到\par 本节开始 } |
\cus_ref_label_box:nn { sec:module-box-prog } 
  { \myparfbox {b} {3cm} { 链接到\par 本节开始 } }
\ExplSyntaxOff
\stopxamplecode
\xampleprint 
\end{xample}
另见 \csref(HyperRef2){HyperRef}、\csref(HyperLink2){HyperLink} 及\cref{eg:hyperref-fparbox}。

\subsection{特殊的“水平”盒子}

\TeX 的 \tn{hbox} 所创建的盒子是\emph{受限水平模式}（restricted horizontal mode）
下的盒子。这盒子中的 discretionary、penalty 等项被移除了，当使用 \tn{unhbox} 时，在某些位置
无法断行。
名为 \href{https://www.tug.org/TUGboat/tb11-4/tb30downes.pdf}{\itshape Line Breaking in \tn{unhbox}ed Text} 的 TUGBoat 文章介绍了一种方法，可以解决上述问题。

本模块使用此种想法定义了几个命令。

\begin{function}{\cus_set_shbox:Nn,\cus_set_shbox:Nw,\cus_set_shbox_end:}
  \begin{syntax}
    \V*|\cus_set_shbox:Nn| \meta{box} \marg{content}
    \V*|\cus_set_shbox:Nw| \meta{box} \meta{content} \V*|\cus_set_shbox_end:|
  \end{syntax}
设置可正常断行的盒子。注意不允许在 \meta{content} 中手动断行。

在 \XeTeX 中，断行位置与 \tn{hbox} 无区别，即不会增加额外的断行位置。以下均同。
\end{function}

\begin{function}{\cus_peek_shbox:Nnw}
  \begin{syntax}
    \V*|\cus_peek_shbox:Nnw| \meta{box} \marg{code} \meta{material}
  \end{syntax}
向后收集可正常断行的盒子。注意不允许在 \meta{content} 中手动断行。
\end{function}

\begin{function}{\cus_use_shbox:N}
  \begin{syntax}
    \V*|\cus_use_shbox:N| \meta{box}
  \end{syntax}
类似于 \cs{box_use:N}，使用上述两个命令设置的盒子必须使用这个命令来使用盒子。
\end{function}

注意在使用了 \cs{cus_set_shbox:Nw} 后如果没有使用 \cs{cus_use_shbox:N}，则不能再使用 \cs{cus_set_shbox:Nw}。

使用这个技术使得超链接文本可以包含特殊文本且能够正常断行。

\begin{function}{\cus_ref_label_shbox:nn,\cus_ref_target_shbox:nn}
  \begin{syntax}
    \V*|\cus_ref_label_shbox:nn| \marg{label} \meta{material}
  \end{syntax}
链接到 \meta{label} 或 \meta{target}。\meta{material} 可以包含特殊文本，可以断行，但不允许手动断行。

另见 \cs{cus_ref_label_box:nn}、\cs{cus_ref_target_box:nn}。
\end{function}



\section{\cusmodule{struct}模块}\label{sec:struct-programming}

以下简单描述 \cusmodule{struct} 模块中目录的数据结构。

\begin{function}{\addcombinedlisttype}
  \begin{syntax}
    \V\addcombinedlisttype \marg{type} \marg{cbl levels}
  \end{syntax}
若要添加新的目录类型，必须先声明 \meta{type}。\meta{cbl levels} 为这个目录类型可用的层级名称，层级名后的中括号括起的数字表示其层级 \meta{level}，也可使用通常的 \texttt{key=val} 的形式。
\end{function}

比如，对于标准的目录 \tn{tableofcontents}，它写入的 \texttt{type} 为 \texttt{toc}，有
\begin{xample}
\addcombinedlisttype{toc}
  { 
    part[-1], 
    chapter[0], 
    section[1], subsection[2], subsubsection[3], 
    sub3section[4], sub4section[5], 
    paragraph[4], subparagraph[5], 
  }
\stopxamplecode
\xamplecode \medskip
\end{xample}

对于标准的 \tn{listoffigures} 和 \tn{listoftables}，有
\begin{xample}
\addcombinedlisttype{lof}{ figure=1 } 
\addcombinedlisttype{lot}{ table=1 } 
\stopxamplecode 
\xamplecode \medskip
\end{xample}

原有的 \tn{addcontentsline} 接受三个参数，其中第一个参数为写入的文件的扩展名，在这里就是
目录项的类型 \meta{type}，其第二个参数就是这里的 \meta{cbl level}，即层级名称，\meta{cbl levels} 应包含这个参数的所有可能值；第三个参数就是 \meta{cbl list entry}。新设置的值将覆盖旧有的值。

绝大多数情况下，无需手动设置它，\cusmodule{struct} 模块会自动设置它们。
%对于 \pkg{float}、\pkg{newfloat}、\pkg{floatrow}、\pkg{algorithm2e}、\pkg{listings}、\pkg{thmtools}、\pkg{tcolorbox} 等宏包。

\begin{function}[EXP]{\retcbltypelevel}
  \begin{syntax}
    \V\retcbltypelevel \marg{type} \marg{cbl level}
  \end{syntax}
展开为 \meta{type} 类型中层级名称 \meta{cbl level} 对应的层级数。前缀 \opt{ret} 为 return。
\end{function}

\begin{function}[EXP]{\retcbltotalcounts}
  \begin{syntax}
    \V\retcbltotalcounts \marg{type}
  \end{syntax}
展开为 \meta{type} 类型的目录条目数。若 \meta{type} 为空，则为各类型的总和。

每个类型的条目数在使用 \csref{enablecombinedlist} 时就已经确定，此后不可更改，
只需常数时间即可获取（包括为空时的情况）。
\end{function}

每个类型的目录条目包含为如下数据：

{\centering \fbox{\parbox{.93\textwidth}{\cs{cus@type@contentsline}
  \marg{type}\marg{cbl count}\marg{info}\marg{level}\hfil\break
  \null\quad\marg{cbl list entry}\marg{thepage}\marg{anchor}\cs{cus@type@contentsline@}}}\par}

\begin{itemize}[nosep]
  \item \cs{cus@type@contentsline}，\cs{cus@type@contentsline@}，这两个宏标记条目的边界；
  \item \meta{type} 为目录类型名；
  \item \meta{cbl count} 为本条目在存储着所有目录项的那个列表中的位置；
  \item \meta{info} 为一个列表，它标记着这个目录项的某些信息，第一项一般为这个目录项层级的名称，其余的项形如 \marg{property}\marg{value}。在 \cs{chapter} 中为 \texttt{chapter}，在 figure 中，为 \texttt{figure}；
  \item \meta{level} 为这个目录项的层级，是一个整数；
  \item \meta{cbl list entry} 为这个目录项的值，通常包含着标题或 caption；若有编号，则为 \tn{numberline}\marg{name}\marg{title}，\meta{name} 是编号，\meta{title} 是标题；若目录项没有编号，则为 \tn{nonumberline}\texttt{\{\}}\marg{title}；若目录项的值中有 \tn{hspace}，则第一个未用花括号包裹的 \tn{hspace} 总是位于 \meta{name} 中；
  \item \meta{thepage} 为目录项所在页的 \tn{thepage}；
  \item \meta{anchor} 为目录项原位置的锚点。仅在 \pkg{hyperref} 宏包加载时有效。可作为 \tn{hyperlink} 命令的第一个参数。
\end{itemize}

每个目录类型不仅分别存储在各自的列表中，还存储在一个统一的列表（以下称为 cbl 列表）中。
在目前的版本中，存储顺序是按照宏的执行顺序而并不一定是文档实际输出顺序（例如，
一个浮动体可能出现在其执行顺序之前）。

cbl 列表除了 \cs{cus@type@contentsline}、\cs{cus@type@contentsline@}、
\meta{cbl count} 改为 \cs{cus@cbl@contentsline}、\cs{cus@cbl@contentsline@}、
\meta{type count} 外，其它未改变。这里的 \meta{type count} 为此条目在其对应类型的列表中的位置。

\begin{function}[EXP]{\retcblentryname}
  \begin{syntax}
    \V\retcblentryname \marg{type} \marg{count}
  \end{syntax}
展开为存储着类型 \meta{type} 第 \meta{count} 项的那个宏的名称。可以使用 \tn{UseName}、\linebreak
\tn{@nameuse} 等获取这个目录项。也可以作为 \LaTeX3 函数的 \texttt{c} 参数，如 \cs{tl_show:c} \verb|{\retcblentryname{}{\retcbltotalcounts{}}}| 在终端中显示 cbl 列表的最后一项。
\end{function}

\begin{function}[EXP]{\retcblentrydata}
  \begin{syntax}
    \V\retcblentrydata \marg{type} \marg{data} \marg{count}
  \end{syntax}
获取 \meta{type} 列表第 \meta{count} 项 \meta{data} 的值。\meta{type} 为空，则获取 cbl 列表中的值。

\meta{data} 可为 \texttt{type}、\texttt{count}、\texttt{info}、\texttt{level}、\texttt{entry}、\texttt{thepage}、\texttt{anchor}。
\end{function}

\begin{function}{\iteratecontents}
  \begin{syntax}
    \V\iteratecontents \marg{type} \marg{inline code}
  \end{syntax}
使用 \meta{inline code} 迭代 \meta{type} 中的每一个目录项。若 \meta{type} 为空，则迭代 cbl 列表。

\meta{inline code} 可使用 7 个参数，分别顺序代表前述的 7 个数据值。
\end{function}

\begin{function}[EXP]{\retcbldefaultlevellistname}
  \begin{syntax}
    \V\retcbldefaultlevellistname \marg{type}
  \end{syntax}
展开为一个 clist 的名称，这个 clist 的前 $n$ 项为 \meta{type} 这个类型中层级为 0 的项的索引（即 \meta{type count} 的值），最后一项为 \verb|\retcbltotalcounts|\marg{type}\verb|+1|。
\end{function}

\begin{function}{\CurrentCombinedListCount}
当写入目录时这个 int 寄存器递增一次。

在此处，它的值为 \number\CurrentCombinedListCount，表示此前最近一次写入的目录是目录文件中的第 \number\CurrentCombinedListCount 项。
\end{function}

\begin{function}{\CurrentTocDefaultLevelCount}
当 \texttt{toc} 类型的目录中添加层级为 0 的条目时这个 int 寄存器递增一次。
如，在使用不带星号的 \cs{chapter} 时将递增 1。

在此处，它的值为 \number\CurrentTocDefaultLevelCount，表示此处的章节是第 \number\CurrentTocDefaultLevelCount 个层级为 0 的章节。
\end{function}

\begin{xample}
\ExplSyntaxOn
\tl_set:Nx \l_tmpa_tl { \retcbldefaultlevellistname {toc} }
\clist_item:cn { \l_tmpa_tl } { \CurrentTocDefaultLevelCount } ,~
\clist_item:cn { \l_tmpa_tl } { \CurrentTocDefaultLevelCount + 1 }
\ExplSyntaxOff
\stopxamplecode
\xampleprint。表示本章节的所有目录项为 toc 类型的目录中的第 32 -- 45 项。
\end{xample}

以下代码输出本章目录。

\begin{xample}
\ExplSyntaxOn
\int_compare:nNnT { \retcbltotalcounts{} } > { 0 }
  {
    \tl_set:Nx \l_tmpa_tl { \retcbldefaultlevellistname {toc} }
    \int_set:Nn \l_tmpa_int % 本章开始
      { \clist_item:cn { \l_tmpa_tl } { \CurrentTocDefaultLevelCount } }
    \int_set:Nn \l_tmpb_int % 下章开始
      { \clist_item:cn { \l_tmpa_tl } { \CurrentTocDefaultLevelCount + 1 } }
    \int_step_inline:nnnn { \l_tmpa_int } { 1 } { \l_tmpb_int - 1 }
      { \tl_use:c { \retcblentryname {toc} {#1} } }
  }
\ExplSyntaxOff
\stopxamplecode
\xamplecode\label{eg:local-toc-prog} \xampleline \vspace*{-\bigskipamount}
\xampletext
\end{xample}

\begin{function}{\getcbllevelrange}
  \begin{syntax}
    \V\getcbllevelrange \marg{type} \marg{level} \oarg{index} \marg{min} \marg{max}
  \end{syntax}
获得在 \meta{type} 目录下，包含 \meta{index} 的那个具有层级 \meta{level} 的块的索引范围。

例如，此处所属的那个章节的范围是 {\getcbllevelrange{toc}{chapter}\fooa\foob $[\fooa,\foob]$。表示此处所在的章在 \texttt{toc} 类型的目录中从第 \fooa 个开始，到第 \foob 个结束。}
\end{function}

\begin{function}{\cus_contents_get:nN,\cus_contents_type_get:nnN}
  \begin{syntax}
    \V*|\cus_contents_get:nN| \marg{cbl count} \meta{tl}
    \V*|\cus_contents_type_get:nnN| \marg{type} \marg{type count} \meta{tl}
  \end{syntax}
将 cbl 的第 \meta{cbl count} 项（或 \meta{type} 的第 \meta{type count} 项）保存至 \meta{tl} 中。如果此项不存在，则为 \cs{q_no_value}。
\end{function}

\begin{function}{\cus_get_heading_level:nnN}
  \begin{syntax}
    \V*|\cus_get_heading_level:nnN| \marg{type} \marg{level name} \meta{tl}
  \end{syntax}
获取 \meta{type} 中 \meta{level name} 的 level 值，如果不存在这样的 \meta{level name}，则为 \cs{q_no_value}。
\end{function}

关于章节标题和目录的详细用法和样例见\cref{ch:title-cbl}。


\section{\LaTeXe 的 mark 机制}\label{sec:ltmarks}

\LaTeXe 在 2022-06-01 的发行版中引入了新的 mark 机制。本节简述这一机制，更详细的说明请参考 \file{ltmarks-doc.pdf}。本说明文档的源码也使用了这个新机制。

\begin{function}[module=mark]{\NewMarkClass,\mark_new_class:n}
  \begin{syntax}
    \V\NewMarkClass \marg{class}
    \V*|\mark_new_class:n| \marg{class}
  \end{syntax}
声明一个新的 mark class。仅能在导言区使用。
\end{function}

\begin{function}[module=mark]{\InsertMark,\mark_insert:nn}
  \begin{syntax}
    \V\InsertMark \marg{class} \marg{text}
    \V*|\mark_insert:nn| \marg{class} \marg{text}
  \end{syntax}
添加 mark 到当前的垂直列中，这个 mark 包含 \meta{text}（被完全展开）。

在不能使用浮动体的地方也无法使用这个命令，如在一个盒子中使用它们时无效。特别的，在 \env{multocols} 等多栏环境中使用它们将无效。
\end{function}

\begin{function}[module=hook point,type=hook point]{insertmark}
在执行 \cs{InsertMark}、\cs{mark_insert:nn} 时，将首先执行 \hook{insertmark} 钩子。
\end{function}

\begin{function}[EXP,module=mark]{\TopMark,\FirstMark,\LastMark,
  \mark_use_top:nn,\mark_use_first:nn,\mark_use_last:nn}
  \begin{syntax}
    \V\TopMark \oarg{region} \marg{class}
    \V*|\mark_use_last:nn| \marg{region} \marg{class}
  \end{syntax}
展开为 \meta{class} 在 \meta{region} 中相应位置的 \meta{text}。

\cs{FirstMark}、\cs{LastMark} 分别展开为 \meta{region} 的第一个、最后一个 \meta{text}。
\goodbreak \cs{TopMark} 展开为上一个 \meta{region} 的最后一个 \meta{text}。

目前，\meta{region} 可选值为 \texttt{page}、\texttt{previous-page}、\texttt{column}、
\texttt{previous-column}。在多栏（双栏）文档中，\texttt{first-column}、
\texttt{last-column} 分别代表最左列和最右列。

\meta{region} 默认为 \texttt{page}。
\end{function}

\begin{function}[EXP,module=mark]{\IfMarksEqualTF,
  \mark_if_eq:nnnnTF,\mark_if_eq:nnnnnnTF}
  \begin{syntax}
    \V\IfMarksEqualTF \oarg{region} \marg{class} \marg{pos_1} \marg{pos_2} \marg{true} \marg{false}
    \V*|\mark_if_eq:nnnnTF| \marg{region} \marg{class} \marg{pos_1} \marg{pos_2} \marg{true} \marg{false}
    \V*|\mark_if_eq:nnnnnnTF| \marg{region_1} \marg{class_1} \marg{pos_1} 
    ~~~~~~~~~~~~~~~~~~~~~\marg{region_2} \marg{class_1} \marg{pos_2} \marg{true} \marg{false}
  \end{syntax}
判断两个 mark 的 \meta{text} 是否完全相等（使用 \tn{ifx}）。

\meta{pos} 为 \texttt{top}、\texttt{first}、\texttt{last} 之一。
\end{function}

原有的 \tn{markboth}、\tn{markright}、\tn{leftmark}、\tn{rightmark} 仍然可用。

可用使用 \texttt{2e-left}、\texttt{2e-right}、\texttt{2e-right-nonempty} class
来获取 \tn{leftmark} 和 \tn{rightmark}。
\texttt{2e-right} 与 \texttt{2e-right-nonempty} 的区别是，后者仅在 \meta{rightmark}
非空时才更新。


\chapter{章节标题和目录}\label{ch:title-cbl}

\CusTeX 重新实现了标题和目录的命令。其中标题的设置方式是 \CTeX 风格的，
输出目录则提供了多种设置风格，例如 \pkg{etoc} 宏包和 \hologo{KOMAScript} 文档类的风格。
可以在正文中任意位置使用任意次这些命令。


\section{title class，标题类}\label{sec:struct-title-class}

标题类规定了一个标题总体上是如何显示的，可以使用一些键值选项来微调具体的显示效果。

\CusLaTeX 预定义了 5 个标题类，分别为：\texttt{page}、\texttt{top}、\texttt{normal}、\texttt{free}、\texttt{wrap}。

用户可以自己定义标题类 \meta{class}。只需定义如下 6 个命令：
\startfullpagewidth<1.7cm>
\begin{itemize}[leftmargin=5cm]
  \item[\cs{title@class@\meta{class}}] 用于显示标题的代码，有两个参数，第一个为标题的名称，第二个表示是否为带星号的标题（用 \cs{title@ifstar}）判断；
  \item[\cs{title@classkeys@\meta{class}}] 此标题类额外的键；
  \item[\cs{title@classinitial@\meta{class}}] 初始值；
  \item[\cs{title@classhook@\meta{class}afterdef}] 钩子，可选，使用此标题类定义标题时，定义结束后执行的代码；
  \item[\cs{title@classhook@\meta{class}begin}] 钩子，可选，此标题开始时要执行的代码，一般用在 \cs{title@class@\meta{class}} 中；
  \item[\cs{title@classhook@\meta{class}end}] 钩子，可选，此标题结束时要执行的代码，一般用在 \cs{title@class@\meta{class}} 中；
\end{itemize}
\stopfullpagewidth


\section{输出\LaTeX 原始风格的目录}

\CusTeX 接管各种目录的输出，如标题、图表目录等。如果要输出任何一种目录，
则必须通过 \csref{enablecombinedlist} 命令启用。这个命令只能使用一次，可以用于导言区
（此时它自动移动到文档开头）和文档开头，使用它以后会读取指定的目录文件。此后方可通过
\tn{tableofcontents} 等命令来输出目录。

默认情况下，使用 \tn{tableofcontents}、\tn{listoffigures}、\tn{listoftables} 就是
\LaTeX 默认的格式。这些命令是对 \csref{multicolplaincombinedlist} 的简单封装，即

\begin{xample}
\DeclareRobustCommand\tableofcontents[1][columns=1]
  {\multicolplaincombinedlist[{#1}]{\contentsname}{toc}}
\DeclareRobustCommand\listoffigures[1][columns=1]
  {\multicolplaincombinedlist[{#1}]{\listfigurename}{lof}}
\DeclareRobustCommand\listoftables[1][columns=1]
  {\multicolplaincombinedlist[{#1}]{\listtablename}{lot}}
\stopxamplecode 
\xamplecode \medskip
\end{xample}

\csref{multicolplaincombinedlist} 则是用于输出默认的多栏目录。可以使用一个可选参数
设置多栏的样式，见\cref{sec:multicol}。

\CusTeX 的目录机制适配了许多宏包，诸如 \pkg{algorithm2e}、\pkg{chemmacros}、
\pkg{listings}、\pkg{thmtools} 等有固定目录扩展名（即目录类型）的宏包，以及
\pkg{float}、\pkg{newfloat}、\pkg{floatrow}、\pkg{tcolorbox} 等可设置目录扩展名的宏包，
既可以直接使用这些宏包自己的输出目录的命令，
也可以使用上述的两个通用的命令来输出目录，前提是知道目录的类型。

受支持的有固定目录扩展名的宏包其扩展名和 \veta{level name} 
如\cref{tab:pkg-cbl-level} 所示，使用诸如 \pkg{float} 宏包创建的不在此列。
暂不支持 \pkg{ntheorem} 宏包。

\begin{table}
\def\theadset{\normalsize\bfseries}
\aboverulesep0pt \belowrulesep0pt 
\begin{tabular}{|l|>{\ttfamily}c|>{\ttfamily}l|l|>{\ttfamily}c|>{\ttfamily}l|}
\toprule 
\thead{宏包\,/\,环境}&\thead{\veta{type}}&\thead{\veta{level name}} & \thead{宏包\,/\,环境}&\thead{\veta{type}}&\thead{\veta{level name}} \\ \midrule 
\pkg{algorithm2e}&loa&algocf & \pkg{chemmacros}&lor&reaction \\ \hline
\pkg{hypdvips}&loa&\makecell[l]{FileAttachment\\EmbeddedFile} & \pkg{musical}&\makecell[l]{los\\lod}&section \\ \hline
\pkg{listings}&lol&\makecell[l]{lol\\lstlistings} & \pkg{pdfcomment}&lpc&lpcsec \\ \hline
\pkg{poetry}&lop&\makecell[l]{poem\\poemgroup} & \pkg{todonotes}&tdo&todo \\ \hline
\pkg{thmtools}&loe&\multicolumn{4}{l|}{由 \tn{newtheorem}、\tn{declaretheorem} 定义的环境名} \\ \hline 
\env{figure}&lof&figure & \env{table}&lot&table \\ \bottomrule 
\end{tabular}
\caption{受支持的宏包和 \env{figure}、\env{table} 环境}\label{tab:pkg-cbl-level}
\end{table}

像 \pkg{float} 等可以自定义浮动环境的宏包，其目录类型 \veta{type} 就是目录的扩展名，
\veta{level name} 就是所定义的浮动环境名。

对于 \pkg{tcolorbox} 宏包，其目录类型就是键 \opt{/tcb/new/list inside} 指定的名称，
\veta{level name} 就是键 \opt{/tcb/new/list type} 指定的值。

这些宏包的 \veta{level name} 的 \veta{level} 值都是 $1$，即与 \tn{section} 同级。
可以使用 \csref{addcombinedlisttype} 修改。

\begin{function}{\settocdepth}
  \begin{syntax}
    \V\settocdepth \marg{整数或层级名称}
  \end{syntax}
设置 \texttt{tocdepth} 计数器的值。可以用来控制目录显示的层级。
\end{function}


\section{使用模板的目录}\label{sec:template-cbl}

前面已经介绍了模板目录的基本使用方法。\texttt{code.name}、\texttt{code.title}、
\texttt{code.leader}、\texttt{code.page}、\texttt{code.hyper} 也能够应付大部分的需要了。

但如果要使模板目录不根据目录层级名来排布，而使用目录层级对应的整数，
由于不同的目录层级名可以对应同一个整数，仅仅使用默认的方式将无法实现这一需求。
本节介绍的内容可以帮助用户定义一个这样的模板目录。

模板目录实际上由名为 \texttt{templatecbl} 的 socket 和 template 这两种抽象结构共同作用。
对于一个模板格式的目录，socket 和 template 是纵向和横向的关系。
\texttt{templatecbl} socket 用于控制模板目录的整体，
而 \texttt{templatecbl} type 用于控制目录项的内容。
关于 socket 的作用和用法请参考 \file{ltsocket-doc.pdf}。

默认的按照目录层级名来排布目录项是名为 \verb|by name| 的 plug，
它限定了模板目录应当如何使用和排布这些目录项。
对应于该 plug 的是名为 \verb|by name| 的 template，
它限定了目录项应当如何排布它的内容。

名为 \texttt{templatecbl} 的 socket 需要 5 个参数，分别为目录类型、
\veta{templatecbl keys}、\veta{multicolumns keys}、目录自动添加的章节标题
以及目录项的范围。用户可为该 socket 定义 plug，它用来规定如何处理目录项。

名为 \texttt{templatecbl} 的 type 需要 7 个参数，
为目录条目保存的内容，前面已经多次提及。
用户可为该 type 定义 template，然后基于 template 定义实例。

如果自定义了标题命令，每个标题命令都会定义一个与命令名相同的目录层级名，
如果要基于 \texttt{by name} template 输出目录，则需要定义同名的实例，
如假如定义了 \verb|\mytitle|，且它与 \tn{section} 同级，则可以使用
\cs{DeclareInstanceCopy} \verb|{templatecbl}{mytitle}{section}| 
直接复制 section 的定义。


\section{\pkg{etoc}风格的目录设置方式}

\pkg{etoc} 宏包提供了 \tn{etocsetstyle} 命令来设置目录，\CusTeX 提供了类似的命令。

\CusTeX 提供了 \csref{tocsetstyle} 和 \csref{specifiedlot} 来分别设置和输出目录，
它们独立于 \csref{tableofcontents} 命令，互不干扰。
对于图片和表格也有类似的命令：\csref{lofsetstyle}、\csref{specifiedlof}、
\csref{lotsetstyle}、\csref{specifiedlot}。
设置目录和输出目录的命令都是分别对一个更加基本的命令的封装：
\begin{xample}
\newcommand{\tocsetstyle}{\SetSpecifiedCombinedListStyle[toc]}
\newcommand{\specifiedtoc}{\SpecifiedCombinedList[toc]}
\newcommand{\lofsetstyle}{\SetSpecifiedCombinedListStyle[lof]{figure}}
\newcommand{\specifiedlof}{\SpecifiedCombinedList[lof]}
\newcommand{\lotsetstyle}{\SetSpecifiedCombinedListStyle[lot]{table}}
\newcommand{\specifiedlot}{\SpecifiedCombinedList[lot]}
\stopxamplecode
\xamplecode \medskip
\end{xample}

因此，只需介绍 \csref{SetSpecifiedCombinedListStyle} 和 
\csref{SpecifiedCom\-bi\-ned\-List} 这两个命令。

以下称由这两个命令制作和输出的目录为“specified cbl”。

每个 specified cbl 的条目的顺序就是保存在目录文件中的顺序。
每个条目都有唯一的层级，把每个条目看成是树中的结点，上层条目其 \veta{level} 值更小，
下层条目其 \veta{level} 值更大。某个条目，设其 \veta{level} 为 $l$，
连同其下（$\veta{level}>l$）的所有条目构成这个条目的块。
在其父条目（在它前面的最近的 \veta{level} 为 $l-1$ 的条目）的块中，
所有 \veta{level} 为 $l$ 的条目的块按其原有的顺序组成一个列表，就是 \veta{level list}。

\csref{SpecifiedCombinedList} 接受一个可选参数 \meta{type}，即目录类型。
用于输出这个目录类型的目录条目。
\csref{SetSpecifiedCombinedListStyle} 有一个可选参数和 6 个必需参数。分别为：
\begin{enumerate}
  \item \meta{type clist}，目录类型的列表；
  \item \meta{level clist}，层级列表，由 \veta{level name} 或数字组成的列表；
  \item \meta{list start}，此层级列表开始时执行的代码；
  \item \meta{block start}，此层级的块开始时执行的代码；
  \item \meta{block item}，此条目执行的代码；
  \item \meta{block finish}，此层级的块结束时执行的代码；
  \item \meta{list finish}，此层级列表结束时执行的代码。
\end{enumerate}
它们的具体位置参见\cref{fig:specified-level}，另见\cref{eg:toc-code-order}。

\marginpar{\normallineskip3pt
  \ffigbox{\setcaptiontype{figure}%
  \fvarbox[t]{12\ccwd}[background-color=yellow9]{%
    $l_0$-i block start\par 
    $l_0$-i block item\par 
    \leavevmode\quad\fparbox[t]{10.3\ccwd}[background-color=green9]{$l_1$ list start\par 
      \fparbox{9.5\ccwd}[background-color=cyan9]{%
        $l_1$-1 block start\par 
        $l_1$-1 block item\par 
        \leavevmode\quad\fvarbox[t]{14\ccwd}[background-color=azure8]{$l_2$ list start\par 
          \fvarbox{14\ccwd}[background-color=blue8]{%
            $l_2$-1 block start\par 
            $l_2$-1 block item\par 
            \leavevmode\quad\fvarbox[t]{14\ccwd}[background-color=magenta9]{$l_3$ list start\par
              \leavevmode\quad$\vdots$\par 
              $l_3$ list finish}\par 
            $l_1$-1 block finish\par}\par 
          \strut\quad$\vdots$\par 
          \fvarbox{14\ccwd}[background-color=blue8]{
            $l_2$-n block start\par 
            $l_2$-n block item\par 
            \leavevmode\quad\fvarbox[t]{14\ccwd}[background-color=magenta9]{$l_3$ list start\par
              \leavevmode\quad$\vdots$\par 
              $l_3$ list finish}\par 
            $l_2$-n block finish\par}\par 
          $l_2$ list finish}\par
        $l_1$-1 block finish}\par 
        \fparbox{9.5\ccwd}[background-color=cyan9]{%
          $l_1$-2 block start\par 
          \leavevmode\quad$\vdots$\par 
          $l_1$-2 block finish}\par 
        \strut\quad$\vdots$\par
        \fparbox{9.5\ccwd}[background-color=cyan9]{%
          $l_1$-m block start\par 
          \leavevmode\quad$\vdots$\par 
          $l_1$-m block finish}\par 
        $l_1$ list finish\par 
      }\par 
    $l_0$-i block finish}}
  {\caption{specified cbl 层级图}\label{fig:specified-level}}}

在这些参数中可以，定义了如下的命令来辅助制作目录：
\begin{description}
  \item[\cmd\tocthename] 标题前的数字。例如：“第一章”、“\S~1”；可能为空，可以使用 \cmd{\tocifnamed} 命令判断；
  \item[\cmd\tocthetitle] 标题。例如本章标题：“章节标题和目录”；
  \item[\cmd\tocthepage] 页码。不一定是数字，可以用 \cmd{\tocifpageisnumber} 判断；
  \item[\cmd\tocifnamed]\marg{true}\marg{false}，判断该目录条目是否有编号；
  \item[\cmd\tocifpageisnumber]\marg{true}\marg{false}，判断页码是否为数字；
  \item[\cmd\toclink]\marg{text}，创建超链接，把 \meta{text} 链接到文档中的对应位置；
  \item[\cmd\toclinkbox]\marg{context}，同上，对于非文字内容也能正确跳转；
  \item[\cmd\tociffirst]\marg{true}\marg{false}，判断当前目录条目是否是其所在的 \veta{level list} 的第一项；
  \item[\cmd\tocifheadentry]\marg{true}\marg{false}，判断当前目录条目是否是此类型目录的第一项，不考虑自动补全的；
  \item[\cmd\tociftailentry]\marg{true}\marg{false}，判断当前目录条目是否是此类型目录的最后一项，不考虑自动补全的；
  \item[\cmd\toctheanchor] 此目录条目在文档中的位置，可作为 \tn{hyperlink}、\csref{HyperLink} 等命令的第一个参数；
  \item[\cmd\tocthelevel] 此条目的层级；
  \item[\cmd\toctheprevlevel] 此条目上一个条目的层级；
  \item[\cmd\tocthenextlevel] 此条目下一个条目的层级；
  \item[\cmd\tocthetype] 此条目所属的目录类型；
  \item[\cmd\toctheclass] 此条目的 \veta{level name}；
  \item[\cmd\toctheindex] 表示该条目在所属的目录类型中是第几项；
  \item[\cmd\tocifcomplement]\marg{true}\marg{code}，specified cbl 会自动补全缺失的层级，该命令用于判断是否是自动补全的；
  \item[\cmd\tocretinfo]\marg{property} 获得目录中的某些特殊信息；
  \item[\cmd\toctheinfo] 目录条目中可能包含的某些特殊信息，为 \text{prop} 类型；
  \item[\cmd\tocthecount] 在 cbl 中的索引；
  \item[\cmd\tocthepreventry] 前一个条目的数据；
  \item[\cmd\tocthenextentry] 后一个条目的数据;
  \item[\cmd\tochilevel] 此目录中最高层级目录的 \veta{level} 值；
  \item[\cmd\toclolevel] 此目录中最低层级目录的 \veta{level} 值，该值数值上不小于 \cs{tochilevel}。
\end{description}
这些命令在每个块中都是有效的，但对于自动补全的层级和 list start、list finish 中则
有效的仅有 \cmd\tocthelevel、\cmd\toctheclass、\cmd\tocifcomplement、\cmd\tocthetype、
\cmd\tochilevel、\cmd\toclolevel，因为其它命令在这些内容中没有意义。

下面这个简单的例子展示了 \csref{SetSpecifiedCombinedListStyle} 的强大能力（尽管它是
用 \csref{tocsetstyle} 设置的）。结果如\cref{fig:powerful-tocsetstyle-result} 所示。

\begin{xample}
\newcommand{\ifinmiddle}[2]{\ifnum\tocthelevel=\tocthenextlevel\relax #1\else #2\fi}
% \usepackage{enumitem}
% \setlist[description]{nosep}
\tocsetstyle 
  {chapter,section}
  {\begin{description}}
  {}
  {\item[\tocifnamed{\tocthename}{\rule{1ex}{1ex}}]
    \tocthetitle\quad\toclink{\tocthepage}\par}
  {}
  {\end{description}}
\tocsetstyle{subsection}
  {\par\begingroup\small\itshape\raggedright【\ }
  {}
  {\tocthename\enskip\tocthetitle
    （\toclink{\tocthepage}）\ifinmiddle{；}{}}
  {}
  {】\par\endgroup\par}
\startmulticolumns[2,ragged]
\specifiedtoc 
\stopmulticolumns
\stopxamplecode
\xamplecode\medskip \label{eg:powerful-tocsetstyle}
\end{xample}

\begin{figure}\RawFloats%[H]%
\startfullpagewidth[outer-sep=0pt,framed=fbox,ragged,overflow=0pt,2]<\bodylmargin>
\newcommand{\ifinmiddle}[2]{\ifnum\tocthelevel=\tocthenextlevel\relax #1\else #2\fi}
\setlist[description]{nosep}
\tocsetstyle 
  {chapter,section}
  {\begin{description}}
  {}
  {\item[\tocifnamed{\tocthename}{\rule{1ex}{1ex}}]
    \tocthetitle\quad\toclink{\tocthepage}\par}
  {}
  {\end{description}}
\tocsetstyle{subsection}
  {\par\begingroup\small\itshape\raggedright【\ }
  {}
  {\tocthename\enskip\tocthetitle（\toclink{\tocthepage}）\ifinmiddle{；}{}}
  {}
  {】\par\endgroup\par}
\specifiedtoc
\caption{\cref{eg:powerful-tocsetstyle} 的结果}
\label{fig:powerful-tocsetstyle-result}
\stopfullpagewidth
\end{figure}

简单解释一下这个例子。首先我们定义了一个命令 \tn[no-index]{ifinmiddle}，
用于判断是否在两个同层级的块中间，这只需 \cmd\tocthelevel 和 \cmd\tocthenextlevel
相等即可。要输出诸如 \env{description} 环境效果的目录，最简单的方法就是使用
\pkg{enumitem} 宏包，然后设置一下间距即可。这里我们使用 \opt{nosep} 将垂直间距设为 
\opt{0pt}。

然后我们在 \opt{chapter} 列的开头和结尾分别插入 \verb|\begin{description}| 和
\\\verb|\end{description}|，然后设置 \veta{block item} 为 \verb|\item|。
这样，每个 \veta{level name} 为 \opt{chapter} 的目录条目都作为 \env{description}
环境的一项。然后把超链接设置到页码上，这样点击页码就能跳转到文档的对应位置。
对 \opt{section} 做同样的事情。

最后，我们设置 \opt{subsection}。首先，在 \opt{subsection} 列的开始处我们修改它的字体，
为了不破坏其它地方的字体，我们把它放到一个组中。此列的开头和结尾分别显示“【”“】”。
之后，我们照常设置标题的页码，并使用括号括住带有超链接的页码。
最后，用到了先前定义的 \tn[no-index]{ifinmiddle}，如果在中间，则插入一个分号分隔。

想要双栏并排显示只需使用 \csref{startmulticolumns}。

下面这个例子展示了本文目录在 \CusTeX 目录机制下的输出顺序。
\begin{xample}
\startmulticolumns[cols=4,ragged,column-sep=10pt]
\newcommand\showthelevel{$l_{\tocthelevel}$ }
\newcommand\showhbarl{\Replicate{2*(\tocthelevel-\tochilevel)}{│}} 
\newcommand\showhbarb{\Replicate{2*(\tocthelevel-\tochilevel)+1}{│}} 
\tocsetstyle{chapter,section,subsection,0,1,2}
  {\showhbarl ┌ \showthelevel list start\par}
  {\showhbarb ┌ \showthelevel block start\par}
  {\showhbarb ├ \showthelevel block item\par}
  {\showhbarb └ \showthelevel block finish\par}
  {\showhbarl └ \showthelevel list finish\par}
\setlength{\parindent}{0pt}
\setlength{\lineskip}{0pt} \setlength{\lineskiplimit}{\maxdimen}
\IfFontExistsTF{TH-Times}{\fontspec{TH-Times}}{}\small
\specifiedtoc 
\stopmulticolumns 
\stopxamplecode
\xamplecode\label{eg:toc-code-order}\medskip 
\end{xample}

\startfullpagewidth
  [cols=4,ragged,column-sep=10pt,framed=fbox,overflow=0pt]<\bodylmargin>
\label{ref:toc-code-order-1}
% \setcaptiontype{figure}
% \captionof{figure}{\cref{eg:toc-code-order} 的结果}
\newcommand\showthelevel{$l_{\tocthelevel}$ }
\newcommand\showhbarl{\Replicate{2*(\tocthelevel-\tochilevel)}{│}} 
\newcommand\showhbarb{\Replicate{2*(\tocthelevel-\tochilevel)+1}{│}} 
\tocsetstyle{chapter,section,subsection,0,1,2}
  {\showhbarl ┌ \showthelevel list start\par}
  {\showhbarb ┌ \showthelevel block start\par}
  {\showhbarb ├ \showthelevel block item\par}
  {\showhbarb └ \showthelevel block finish\par}
  {\showhbarl └ \showthelevel list finish\par}
\setlength{\parindent}{0pt}
\setlength{\lineskip}{0pt} \setlength{\lineskiplimit}{\maxdimen}
\IfFontExistsTF{TH-Times}{\fontspec{TH-Times}}{}\small
\specifiedtoc 
\label{ref:toc-code-order-2}
\vpagerefcompare{ref:toc-code-order-1}{ref:toc-code-order-2}{}
  {\makebox[\columnwidth][r]{（接上页）}}
\stopfullpagewidth

下例展示了为目录添加盒子和边距的方法：
\begin{xample}
\startmulticolumns[2,ragged]

\colorlet{tocgreen}{green!70!black}
\newcommand{\tochyperpage}{\toclink{\tocthepage}}
\hypersetup{hidelinks}

\makeatletter
\tocsetstyle {chapter,0}
  {}
  {\noindent}
  {\fparbox{\linewidth}[border-color=tocgreen, background-color=tocgreen,
    padding={0pt,\fboxsep}]
    {\bfseries\large\raggedright \hangindent4\ccwd \hangafter1 \color{white}%
      \strut \tocifnamed{\tocthename\quad\kern-.3em}{}\tocthetitle
      \breakablefiller[space]\tochyperpage \strut\par}\par }
  {\smallskip}
  {}
\tocsetstyle {section,1}
  {\smallskip
    \begin{list}{}{\leftmargin3\ccwd \labelsep\z@ 
      \itemindent-\ccwd \listparindent\itemindent
      \topsep\z@ \partopsep\z@ \itemsep\z@ \parsep\z@ \parskip\z@}}
  {\item \begingroup\color{tocgreen}\bfseries}
  {\tocifnamed{\tocthename\quad}{}\tocthetitle\breakablefiller[space]%
    \makebox[1.5em][r]{\tochyperpage\;}\par }
  {\endgroup}
  {\end{list}}
\tocsetstyle {subsection,2}
  {}
  {\begingroup\color{black}\bfseries}
  {\tocifnamed{\tocthename\quad}{}\tocthetitle\breakablefiller[dotted]%
    \makebox[1.5em][r]{\tochyperpage\;}\par }
  {\endgroup}
  {}
\makeatother 

\specifiedtoc

\stopmulticolumns
\stopxamplecode
\xamplecode\medskip
\end{xample}

\startmulticolumns[2,ragged,outer-sep=0pt,framed=lfbox,overflow=0pt,
  adj-outer=-\dimeval{\paperwidth-3.4cm-\textwidth}]
\colorlet{tocgreen}{green!70!black}
\newcommand{\tochyperpage}{\toclink{\tocthepage}}
\hypersetup{hidelinks}
\makeatletter
\tocsetstyle {chapter,0}
  {}
  {\noindent}
  {\fparbox{\linewidth}[border-color=tocgreen, background-color=tocgreen, padding={0pt,\fboxsep}]
    {\hangindent4\ccwd \hangafter1 \color{white}\bfseries\large
      \strut \tocifnamed{\tocthename\quad\kern-.3em}{}\tocthetitle
      \breakablefiller[space]\tochyperpage\par}\par }
  {\smallskip}
  {}
\tocsetstyle {section,1}
  {\smallskip
    \begin{list}{}{\leftmargin3\ccwd \labelsep\z@ 
      \itemindent-\ccwd \listparindent\itemindent
      \topsep\z@ \partopsep\z@ \itemsep\z@ \parsep\z@ \parskip\z@}}
  {\item \begingroup\color{tocgreen}\bfseries}
  {\tocifnamed{\tocthename\quad}{}\tocthetitle\breakablefiller[space]%
    \makebox[1.5em][r]{\tochyperpage\;}\par }
  {\endgroup}
  {\end{list}}
\tocsetstyle {subsection,2}
  {}
  {\begingroup\color{black}\bfseries}
  {\tocifnamed{\tocthename\quad}{}\tocthetitle\breakablefiller[dotted]%
    \makebox[1.5em][r]{\tochyperpage\;}\par }
  {\endgroup}
  {}
\makeatother 
\specifiedtoc
\stopmulticolumns



还可使用 \cs{LocalSpecifiedCombinedList} 来输出局部目录。
\cs{localspeci\-fiedtoc} 是一个特例，用来输出局部章节的目录。
这个命令除了有一个用来设置目录类型的可选参数外，还支持修改局部目录的层级和条目的位置。

例如，此处处在在 \tn{section} 中，输出的局部目录为此 \tn{section} 小节的目录。
但可以设置 \cs{localspecifiedtoc}\texttt{(chapter)} 来输出本章节的目录。
还可以通过修改条目的位置来修改输出的章节。例如 
\cs{localspecifiedtoc}\texttt{(chapter,\string\?-4)} 则是输出的本节目录条目
往前数第 4 个条目所在章的目录。这里 \verb|\?| 就是 \csref{Curr\-entCombinedListCount}。
还有一个变量 \verb|\$|，它表示此刻在该类型的目录中的项数。

关于局部章节目录，另见\cref{eg:local-toc-prog}。

下例输出本章目录。

\begin{xample}
\begingroup 
\tocsetstyle {chapter,0}
  {}{\startmulticolumns[ragged,outer-sep=0pt,
      heading={{\centering\Large\bfseries\tocthetitle\par}}]}
  {}
  {\stopmulticolumns}{}
\tocsetstyle {section,subsection}
  {\begin{description}}
  {}
  {\tocifcomplement{\item[\rule{1ex}{1ex}]\rule{1ex}{1ex}\par}
    {\item[\tocifnamed{\tocthename}{\rule{1ex}{1ex}}]
      \tocthetitle\quad\toclink{\tocthepage}\par}}
  {}
  {\end{description}}
\localspecifiedtoc(chapter)
\endgroup
\stopxamplecode
\xampleprint
\end{xample}


\section{目录的内部处理方式}

在 \CusTeX 中，如果要输出某类目录，则必须先通过 \csref{addcombinedlisttype} 注册它，
这个命令接受两个参数，第一个参数为 \veta{type}，表示添加的这个目录类型。
对于标题目录，则为 \texttt{toc}，对于图片、表格目录则分别为 \texttt{lof}、\texttt{lot}，
它就是 \LaTeX 标准目录输出方式里的 \veta{ext}，即输出目录的文件扩展名。
由于 \CusTeX 把所有类型的目录都写入到同一个文件中，因此，\veta{type} 
用于唯一区分不同的目录类型。

同一个目录中，可以有不同的层级，同一层级也可以有所区分。

在 \CusTeX 中，用于区分不同层级的
就是 \veta{level} 变量，它是一个整数，数值小的，层级越高。
例如，对于 \texttt{toc} 目录，在默认情况下，\tn{part} 为 $-1$，\tn{chapter} 为 $0$，
\tn{section} 为 $1$，依此类推。
对于图表目录，默认只有一个层级，为 $1$。

对于同一个层级，用以区分的就是不同的层级名 \veta{level name}。
写入目录时就是根据层级名写入的，目录项首先根据 \veta{level name} 进行分类，
然后再根据这些 \veta{level name} 所属的 \veta{level} 分类。
每个类型的目录项的类别必须属于这些此类型的 \veta{level name} 之一。
因此每个类型的目录其 \veta{level name} 必须完整。

例如，默认情况下，\texttt{toc} 类型的目录条目都是由 \tn{part}、\tn{chapter}、
\tn{section}、\tn{subsection}、\tn{subsubsection}、\tn{paragraph}、\tn{subparagraph} 
这些命令之一写入的，并且它们都是可区分的。因此 \texttt{toc} 的 \veta{level name} 至少
有 $7$ 个，分别代表这 $7$ 种之一。

在标准文档类下，\LaTeX 通过 \tn{l@\meta{name}} 来唯一区分这些 \veta{level name}。
但在 \CusTeX 宏集中，每个 \veta{level name} 只需在其所属的那个目录类型中唯一，
不同目录类型可以有相同的 \veta{level name}。

例如，对于图片类型的目录 \texttt{lof}，其 \veta{level name} 为 \texttt{figure}，
但你尽可以把它设置成 \veta{section}，但写入目录文件中的目录条目也必须随之修改。

尽管看起来很复杂，但实际上以上这些工作在绝大多数情况下 \CusTeX 都会自动完成它们，
无需手动设置它。


\chapter{库的文档接口}

\section{索引，\cuslibrary{index}库}

\CusTeX 提供了添加多个索引的方法。并且能够自动编译索引文件。

目前暂未提供 \pkg{splitidx} 宏包的功能，也不与其兼容。

应该与 \pkg{glossaries} 宏包兼容。

\begin{function}{\newindextype,\setupindex}
  \begin{syntax}
    \verb|\newindextype| \oarg{index keys} \marg{index type}
    \verb|\setupindex|   \oarg{index type list} \marg{index keys}
  \end{syntax}
\cs{newindextype} 创建一个新的索引 \meta{index type}。
\cs{setupindex} 配置 \meta{index type list}。
\meta{index type} 可以使用 \tn{empty} 作为名称，此时它的名称为空。
\end{function}

\begin{function}{\makeindex}
  \begin{syntax}
    \verb|\makeindex|
    \verb|\makeindex| \oarg{index keys}
    \verb|\makeindex| \oarg{index keys} \oarg{index type}
  \end{syntax}
\LaTeX 原有的接口。默认创建名称为空的索引。
\end{function}

\meta{index keys} 不同于 \CusTeX 中其它的键值选项，仅具有类似的接口。

\begin{itemize}[nosep]
  \item \texttt{filename}：索引文件名，一般以 \texttt{idx} 结尾，如果未设置，则为：
  \\\tn{jobname}\texttt{@}\meta{index type}\texttt{.idx}；
  \item \texttt{output}：编译后的索引文件，一般以 \texttt{ind} 结尾，如果未设置，则将
  \texttt{filename} 的后缀修改为 \texttt{ind} 作为输出文件名；
  \item \texttt{name}：如果设置，则应与 \meta{index type} 一致；
  \item \texttt{title}：索引的标题，如 \tn{indexname}；
  \item \texttt{program}：编译索引的可执行程序；如 \texttt{makeindex}、\texttt{makeglossaries}；
  \item \texttt{options}：编译索引时的选项，索引文件名和输出文件名将自动添加；
  \item \texttt{exec}：终端中实际编译索引执行的代码，如果未设置，则组合 \texttt{program} 及 \texttt{options}；
  \item \texttt{auto}；布尔值，是否自动编译索引文件；
  \item \texttt{multi}：多栏选项（\meta{multicolumns key-val}）；
  % \item \texttt{heading}：标题层级；
  \item \texttt{heading*}：标题命令，如 \verb|\chapter[numbering=false]|；
  \item \texttt{init}：索引开头的初始化设置；索引文件不存在时不会执行；
  \item \texttt{prologue}：索引开头的文字；索引文件不存在时不会输出。
\end{itemize}

\smallskip 
\begin{xample}
\newindextype[
  filename=\jobname.docusage.idx, 
  output=\jobname.docusage.ind,
  exec={makeindex -s gind.ist -o \jobname.docusage.ind \jobname.docusage.idx},
  title={代码索引}, heading*={\section}, 
  multi={2, ragged, sep=1em, outer-sep=0pt},
  auto=true
]{docusage}
\stopxamplecode
\xamplecode
\medskip 
\end{xample}

\begin{function}{\setindexinit,\setindexprologue}
  \begin{syntax}
    \verb|\setindexinit| \marg{code}
    \verb|\setindexinit| \oarg{index type} \marg{code}
    \verb|\setindexprologue| \marg{content}
    \verb|\setindexprologue| \oarg{index type} \marg{content}
  \end{syntax}
设置索引开头的初始化设置、设置索引开头的文字。只要使用了 \cs{printindex}，当索引不存在时它们也会执行或输出。

默认设置名称为空的索引。

在初始化代码中还可以重定义索引环境 \env{theindex}。
\end{function}

\begin{function}{\index,\printindex}
  \begin{syntax}
    \verb|\index| \marg{index item}
    \verb|\index| \oarg{index type} \marg{index item}
    \verb|\printindex|
    \verb|\printindex| \oarg{index keys}
  \end{syntax}
\cs{index} 添加索引项 \meta{index item} 到索引 \meta{index type} 中，默认添加到名称为空的索引中；\cs{printindex} 输出索引，以 \texttt{name} 键标识要输出的索引，否则输出名称为空的索引。\meta{index keys} 为前述的键。
\end{function}


\section{\cuslibrary{pgf}库}

\cuslibrary{pgf} 库使用 \pkg{pgf} 宏集的功能，定义了一些命令。

\cuslibrary{pgf} 库\emph{不会}自动加载 \pkg{pgf} 和 \pkg{tikz} 宏包，需要用户自行加载。

\begin{function}{\pgfdeclareimagep,\pgfimagep}
  \begin{syntax}
    \V\pgfdeclareimagep \oarg{image options} \marg{image name} \marg{filename}
    \V\pgfimagep \oarg{image options} \marg{filename}
  \end{syntax}
它们的功能和 \tn{pgfdeclareimage}、\tn{pgfimage} 相同。

它们会自动查找 \tn{setgraphicspath} 设置的路径，且可以自动补全文件扩展名。
\end{function}

\subsection{文字渐变}

需要加载 \pkg{tikz} 宏包。

\begin{function}{\shadetext,\shadetextbox,\shadecontent,\shadecontentbox}
  \begin{syntax}
    \V\shadetext    \marg{shade options} \marg{text}
    \V\shadetextbox \marg{shade options} \marg{material}
    \V\shadecontent    \oarg{node options} \marg{shade options} \marg{content}
    \V\shadecontentbox \oarg{node options} \marg{shade options} \marg{material}
  \end{syntax}
这几个命令用于为文字增加渐变效果。\tn{shadecontent} 和 \tn[break-at-any]{shadecontentbox}
需要阅读器支持才能正确显示。

\tn{shadetextbox} 和 \tn{shadecontentbox} 的 \meta{material} 的参数可以包含特殊文本，如 
verbatim 和分段。

\tn{shadetext} 和 \tn{shadetextbox} 中，仅文字才会显示渐变效果，其它内容如方框不会显示。
\tn{shadecontent} 和 \tn{shadecontentbox} 中，可见的内容都会显示渐变效果。

它们的内容\emph{不能}断行，正如 \tn{fcolorbox} 和 \tn{tcbox} 那样。
\end{function}

\begin{xample}
\shadetext{left color=red,right color=blue}{\bfseries shaded}
\shadetextbox{left color=red,right color=blue}{\verb|\relax|}

\shadecontent{left color=red,right color=blue}{\bfseries shaded}
\shadecontentbox{left color=red,right color=blue}{\verb|\relax|}

\shadetext{left color=red,right color=blue}{\fbox{shaded}} % 方框不会显示
\shadecontent{left color=red,right color=blue}{\fbox{shaded}}

% \usetikzlibrary{shadings}
\shadecontent{shading=color wheel}{%
  \tikz\node[inner sep=0pt,outer sep=0pt,circle,draw,line width=1pt]
    {\parbox{4em}{\linespread{1}\selectfont \centering
      使用\\色轮作为\\渐变}};}
\stopxamplecode
\xampleprint

理想效果见\cref{fig:shade-text} 和\cref{fig:shade-text-white}。
\end{xample}

\begin{figure}
\begin{floatrow}
  \ffigbox{\includegraphics{shade-text}}
    {\caption{透明背景下的理想效果}\label{fig:shade-text}}%
  \ffigbox{\includegraphics{shade-text-white}}
    {\caption{白色背景下的理想效果}\label{fig:shade-text-white}}
\end{floatrow}
\end{figure}

\begin{keyval}[path=/tikz]{tangent,at tangent}
  \begin{syntax}
    tangent=\meta{pos}
    tangent=\meta{name} at\meta{pos}
    tangent=\meta{name} at\meta{pos} and ...
    at tangent=\meta{name}
  \end{syntax}
\keyref[/tikz]{tangent} 用于记录一条路径在 \meta{pos} 的切点坐标及切线方向，
切点坐标为 \opt{(tangent point}~\meta{name}\opt{)}。

\keyref[/tikz]{at tangent} 用于把该路径或 \texttt{scope} 的坐标系变换至指定的位置和方向。

本功能需要自行加载名为 \texttt{decorations.markings} 的 \texttt{pgf} 库。\meta{pos}
和坐标系的方向与 \texttt{/tikz/decoration/mark} 的可用值 
\texttt{at position \meta{pos}with\meta{code}} 一致。
\end{keyval}

\subsection{在背景和前景中使用 {\TikZ} 绘制}

需要加载 \pkg{tikz} 宏包。

本库为 \cs{background} 和 \cs{foreground} 命令的 \meta{位置} 参数增加了一个可用值 
\texttt{tikz}。可以在 \meta{code} 中使用 \TikZ 绘图代码，坐标原点为纸张\emph{左下角}。
并预先定义了几个 node，它们只能在 \cs{foreground} 和 \cs{background} 命令中使用：
\begin{itemize}
  \item[\texttt{page|paper}] 该 node 占满整个纸张，四角为纸张四角；
  \item[\texttt{layout}] 该 node 占满整个 layout，四角为 layout 四角；
  \item[\texttt{text}] 该 node 占满整个正文区域，四角为正文的四角；
  \item[\texttt{margin}] 该 node 占满整个旁注区域，四角为旁注四角；
  \item[\texttt{header}] 该 node 占满整个页眉，包括左右的偏移量，四角为页眉四角；
  \item[\texttt{footer}] 该 node 占满整个页脚，包括左右的偏移量，四角为页脚四角。
\end{itemize}
相比于 \pkg{tikzpagenodes} 提供的 node，它们只需编译一次，且页眉页脚包含偏移量。

\needspace{2\baselineskip}
\begin{xample}
\background(tikz){
  \draw[thick,help lines] (layout.south west) grid (layout.north east);
  \draw[thick,blue] (layout.center) circle (2cm);
  \draw[thick,red] (page.north west)--(page.south east);
  \draw[thick,blue] (layout.north east)--(layout.south west);
  \draw[thick,cyan] (text.south west)rectangle(text.north east);
  \draw[thick,cyan] (margin.south west)rectangle(margin.north east);
  \draw[thick,cyan] (header.south west)rectangle(header.north east);
  \draw[thick,cyan] (footer.south west)rectangle(footer.north east);
}
\stopxamplecode
\xampleprint
如本页所示。
\end{xample}


\section{\cuslibrary{tcb}库}

\cuslibrary{tcb} 库使用 \pkg{tcolorbox} 宏包的功能，定制了一些其它功能。用户
\emph{需自行加载} \pkg{tcolorbox} 宏包。

\subsection{\texttt{multicolumns/framed=tcbox}}\label{sec:multicolumns/framed=tcbox}

本库提供为多栏文字的外框提供了 \opt{tcbox} 可选值。表示用 \pkg{tcolorbox} 宏包的 
\tn{tcbox} 作为盒子外框。\keyref[multicolumns]{framed-options} 可以使用 \pkg{tcolorbox}
宏包提供的选项。

% \begin{keyval}[path=frame]{tcbox-frame,tcbox-first,tcbox-middle,tcbox-last,tcbox-whole}
%   \begin{syntax}
%     tcbox-frame = <{tcolorbox keyval}>
%   \end{syntax}
% \end{keyval}


\section{\cuslibrary{logo}库}

本库使用 \pkg{hologo} 宏包定义了一些 logo 命令。

\begin{pagecenterbox}
\def\plogo#1{\texttt{\string#1}&#1}
\ExplSyntaxOn
\cs_set:Npn \plogos #1
  {
    \tl_clear:N \l_tmpa_tl
    \int_zero:N \l_tmpa_int
    \tl_map_inline:nn {#1}
      {
        \int_incr:N \l_tmpa_int
        \int_compare:nNnTF { \int_mod:nn { \l_tmpa_int } { 4 } } = { 0 }
          { \tl_put_right:Nn \l_tmpa_tl { \plogo {##1} \\ } }
          { \tl_put_right:Nn \l_tmpa_tl { \plogo {##1} & } }
      }
    \int_case:nn { \int_mod:nn { \l_tmpa_int } { 4 } }
      {
        { 0 } { }
        { 1 } { \tl_put_right:Nn \l_tmpa_tl { \plogo{~}&\plogo{~}&\plogo{~} \\ } }
        { 2 } { \tl_put_right:Nn \l_tmpa_tl { \plogo{~}&\plogo{~} \\ } }
        { 3 } { \tl_put_right:Nn \l_tmpa_tl { \plogo{~} \\ } }
      }
    \tl_use:N \l_tmpa_tl
  }
\ExplSyntaxOff
\noindent
\begin{tabular}{llllllll}
\toprule
\plogos{
\ApLaTeX
\ApTeX
\BibTeX
\ConTeXt
\CusLaTeX
\CUSLATEX
\CusTeX
\CUSTEX
\dvipdfmx
\eTeX
\iniTeX
\LaTeX
\LATEX
\LaTeXe
\LATEXe
\LaTeXiii
\LaTeXTeX
\LuaHBTeX
\LuaLaTeX
\LuaMetaTeX
\LuaTeX
\LyX
\METAFONT
\MetaFun
\METAPOST
\MiKTeX
\pdfLaTeX
\PDFLaTeX
\pdfTeX
\plainTeX
\pTeX
\pupTeX
\TeX
\TEX
\TeXLive
\TikZ
\upLaTeX
\upTeX
\XeLaTeX
\XeTeX
}\bottomrule
\end{tabular}
\end{pagecenterbox}
它们可以直接作为章节命令的参数，以及 \cs{MakeUppercase}、\cs{MakeLowercase}、\cs{MakeTitlecase} 的参数。


\section{\cuslibrary{doc}库}

\cuslibrary{doc} 库用于支持排版说明文档。本库移植修改自 \cls{l3doc} 和 \cls{ctxdoc}。

当加载本库时，将创建两个索引，名为 \texttt{docusage} 和 \texttt{docchange}，分别记录代码和
版本历史。使用 \cs{PrintUsages}、\cs{PrintChanges} 分别输出代码索引和版本历史。
可使用 \opt{no-index-file} 库选项来阻止创建它们。

本库的详细用法可参考本说明文档的源码。

本库提供 \cs{cs}、\cs{cmd}、\cs{tn} 来排版宏。

\begin{function}{\cs,\cmd,\tn,\key,
  \cus@doc@cs@format,\cus@doc@cmd@format,\cus@doc@tn@format,\cus@doc@key@format}
\begin{syntax}
  \verb|\cs|  \oarg{cmd key-val} \marg{macro name}
  \verb|\cmd| \oarg{cmd key-val} \marg{macro}
  \verb|\tn|  \oarg{cmd key-val} \marg{tex macro name}
  \verb|\key| \oarg{cmd key-val} \marg{key name}
\end{syntax}
排版宏。\cs{tn} 专用于排版 \TeX 和 \LaTeXe 的宏。

\cs{cus@doc@cs@format} 等命令则是修改显示的格式，最多可带有一个参数。
这些命令中可以修改链接显示的颜色和字体。
\end{function}

\begin{keyval}[path=doc/cmd]{index,module,no-index,space,keyval,hyper,target,type}
\begin{syntax}
  index    = <{index entry}> &#
  module   = <{module name}> &#
  no-index = <&\TTF> &! false 
  do-index = <&\TTF>  &#
  space    = <&\TTF> & false 
  keyval   = <&\TTF> &#
  hyper    = <&\TTF|raw> & true 
  no-hyper &&
  target   = <{target}> &#
  target*  = <{target}>
  type     = <{label type}>
\end{syntax}
\csreflist{cs,cmd,tn} 可用的键值选项。
\end{keyval}

\opt{no-index} 控制是否写入索引文件。\opt{do-index} 与 \opt{no-index} 相反。

\opt{space} 控制是否将空格替换为 \tn{textvisiblespace}。

\opt{keyval} 控制是否为键值选项。

\opt{hyper} 控制是否自动链接到该命令的说明处，仅当该命令的说明存在时有效。
\opt{target} 设置链接的位置。默认情况下，还会对 \meta{target} 进行一定的处理，
如果已经可以确定无需再对 \meta{target} 进行处理，可以设置 \verb|hyper=raw|。
\opt{target*=} 是 \verb|hyper=raw, target=| 的简写。

\opt{module} 设置的是在索引中归属的类别，对于 \LaTeXiii 的命令，可以自动检测其 
\veta{module}，若要把某些命令归类到特定的 \veta{module} 中，则需要设置此键。

\opt{type} 用于设置自动添加的 \veta{label} 的类别，命令默认为 \texttt{function}，
键名默认为 \texttt{keyval}，环境为 \texttt{environment} ，如若修改了此值，
在引用时也必须修改。

\veta{module} 有几个特殊的值 \verb*|hook point|、\verb*|color name|，
分别用于标记此命令为钩子、颜色名称。使用这几个值时最好也将 \opt{type} 也改为相同的值。

\begin{keyval}[path=doc/cmd]{hyphen-opacity}
  \begin{syntax}
    hyphen-opacity = <{0--1之间的数}>
  \end{syntax}
设置断行字符的透明度。
\end{keyval}

\begin{keyval}[path=doc/cmd]{break-at-any}
  \begin{syntax}
    break-at-any = <&\TTF> & false 
  \end{syntax}
设置是否在任何位置都可断行。设置此选项为真时会增加编译时间，尽可能仅在必要时使用。
\end{keyval}

\begin{function}{\meta,\veta,\marg,\Arg,\oarg,\parg,\pkg,\env,\cls,\opt,\file,\docfile,
  \cus@doc@meta@format,\cus@doc@veta@format,\cus@doc@marg@format,\cus@doc@oarg@format,
  \cus@doc@parg@format,\cus@doc@pkg@format,\cus@doc@env@format,\cus@doc@cls@format,\cus@doc@opt@format} 
排版宏的参数。

\cs{cus@doc@meta@format} 等命令用于修改显示的格式。

\cs{Arg} 相当于 \cs{marg}。

\tn{file} 和 \tn{docfile} 相当于 \tn{nolinkurl}。不提供修改格式的接口。

注意 \cs{marg}、\cs{oarg}、\cs{parg} 都包含 \cs{meta}。
\end{function}

\begin{function}[type=environment]{function}
\begin{syntax}
  \verb|\begin|\{function\} \oarg{function key-val} \marg{functions clist}
  ~~... 
  \verb|\end|\{function\}
\end{syntax}
显示函数说明。\meta{functions clist} 可以是宏或者环境名，不可包含多余的空格以及注释符。
\end{function}

\begin{function}[type=environment]{keyval}
\begin{syntax}
  \verb|\begin|\{keyval\} \oarg{function key-val} \marg{keys clist}
  ~~... 
  \verb|\end|\{keyval\}
\end{syntax}
显示键值选项的说明。\meta{keys clist} 为键列表，不可包含多余的空格以及注释符。
\end{function}

\begin{function}[type=environment]{syntax}
\begin{syntax}
  \verb|\begin|\{syntax\}
  ~~... 
  \verb|\end|\{syntax\}
\end{syntax}
输出使用方法。

本环境中每个输入行都为一个输出行（一个段落），除每行首尾的空格被移除外，所有的空格都被保留下来；
此外，可使用 \V{~} 输出一个空格的宽度。
\end{function}

本环境中可以使用几个特殊的字符（字符对），它们是语法糖：
\begin{itemize}[nosep]
  \item \verb|<...>| --- 在文本环境时这相当于 \verb|\meta{...}|，数学环境时仍然为小于、大于号；但有几个例外：
  \item \verb|<&...>| --- 当文本环境中 \verb|<| 紧跟 \verb|&| 时，\verb|...| 被视为可选值；
  \item \verb|<{...}>| --- 当文本环境中 \verb|<...>| 中的内容为一个正确嵌套的组时，它被视为 \verb|\marg{...}|；
  \item \verb|&| --- 其后的值被认为是初始值，每行最多应仅使用一次，与之等价的写法是： \cs{initialval}\verb| ...|（无需花括号）；但有几个例外：
  \item \verb|&*| --- 当 \verb|&| 紧跟 \verb|*| 时，相当于 \cs{repinitval}，可自行设置文字；
  \item \verb|&&| --- 当 \verb|&| 紧跟 \verb|&| 时，相当于 \cs{forbiddenval}，表示禁止设置值；
  \item \verb|&#| --- 当 \verb|&| 紧跟 \verb|#| 时，相当于 \cs{automaticval}，表示如未给出将自动设置值；
  \item \verb|&~| --- 当 \verb|&| 紧跟 \verb|~| 时，相当于 \cs{initemptyval}，表示初始为空；
  \item \verb|&!| --- 当 \verb|&| 紧跟 \verb|!| 时，相当于 \cs{resetval}，表示在对应命令或环境中其值均被重设；
  \item \verb+|+ --- 相当于“\orbar”（\cs{orbar}），一般用于分隔不同的可选值；
  \item \verb|(...)| --- 这中间的值被认为是默认值，以粗体显示，与之等价的写法是：\\ \verb|\defaultval{...}|。
\end{itemize}

\begin{texnote}
  \verb|#| 在本环境中的类别码被设置为 12（other），
  \texttt{\string<\string|\string(\~{}\&} 的类别码为 13（active）。
\end{texnote}

\envreflist{function,keyval,syntax} 环境均可使用 \cs{V} 命令，它和 \cs{Verbati\-mize} 
一样，但以当前字体显示。

\begin{keyval}[path=doc/function]{EXP,rEXP}
\begin{syntax}
  EXP &&
  rEXP &&
\end{syntax}
\opt{EXP} 将函数标记为\emph{完全可展的}（fully expandable functions），可同时用作 \verb|x|、\verb|e|、\verb|f| 类型的参数。如 \tn{string}、\cs{cs_to_str:N}。使用 \UseName{\detokenize{__cus_doc_typeset_exp:}} 标记。

\opt{rEXP} 将函数标记为\emph{受限可展的}（restricted expandable functions），
这些函数是完全可展的，但不能在 \verb|f| 类型的参数中完全展开（cannot be fully expanded）。
如 \cs{seq_map_function:NN}。使用 \UseName{\detokenize{__cus_doc_typeset_rexp:}} 标记。
\end{keyval}

\begin{keyval}[path=doc/function]{TF,pTF,noTF}
  \begin{syntax}
    TF &&
    pTF &&
    noTF &&
  \end{syntax}
标记函数为是带有真假值参数的函数。

\opt{TF} 将函数标记为带有真假参数的函数，如 \cs{tl_if_eq:nnTF}。
\opt{pTF} 在 \opt{TF} 的基础上，还将函数标记为带有可用于 \cs{if_predicate:w} 的函数。
\opt{noTF} 在 \opt{TF} 的基础上，还将函数标记为不带真假参数的函数，如 \cs{prop_get:NnN}。
\end{keyval}

\begin{keyval}[path=doc/function]{added,updated}
  \begin{syntax}
    added   = \{\meta{年}-\meta{月}-\meta{日}\} 或 \{\meta{年}/\meta{月}/\meta{日}\}
    updated = \{\meta{年}-\meta{月}-\meta{日}\} 或 \{\meta{年}/\meta{月}/\meta{日}\}
  \end{syntax}
此函数是何时添加的或最近一次修改在何时。
\end{keyval}

\begin{keyval}[path=doc/function]{label,label*,no-label}
  \begin{syntax}
    label  = \marg{label clist}
    label* = \marg{label clist}
    no-label &&
  \end{syntax}
设置 \tn{label}。\opt{label} 不会设置默认的 label，\opt{label*} 会设置默认的 label。
\end{keyval}

\begin{keyval}[path=doc/function]{verb}
  \begin{syntax}
    verb &&
  \end{syntax}
将整个 \meta{functions clist} 或 \meta{keys clist} 看作是一个函数或键。
\end{keyval}

\begin{keyval}[path=doc/function]{module}
  \begin{syntax}
    module = \marg{module name}
  \end{syntax}
设置当前函数所在的模块。
\end{keyval}

\begin{keyval}[path=doc/function]{type}
  \begin{syntax}
    type = \marg{类型}
  \end{syntax}
设置当前的类型，如 \texttt{function}、\texttt{environment}、\texttt{keyval}。
\end{keyval}

% \begin{keyval}[path=doc/function]{keyval}
%   \begin{syntax}
%     keyval = <&\TTF>
%   \end{syntax}
% 是否是键值参数。
% \end{keyval}

\begin{keyval}[path=doc/function]{path}
  \begin{syntax}
    path = <{key path}>
  \end{syntax}
设置键值参数的键路径。
\end{keyval}

\begin{keyval}[path=doc/function]{frame,frame+}
  \begin{syntax}
    frame  = \marg{frame key-val}
    frame+ = \marg{frame key-val}
  \end{syntax}
设置外部方框盒子的选项。
\end{keyval}

\begin{function}[type=environment]{texnote}
  \begin{syntax}
    \verb|\begin|\{texnote\}
    ~~... 
    \verb|\end|\{texnote\}
  \end{syntax}
\end{function}

\begin{function}{\csref,\csreflist,\envref,\envreflist,\keyref,\keyreflist,
  \cus@doc@csref@format,\cus@doc@envref@format,\cus@doc@keyref@format}
  \begin{syntax}
    \verb|\csref|      \oarg{type} \marg{cs name}
    \verb|\csref|      \oarg{type} \parg{cs label} \marg{cs name}
    \verb|\csreflist|  \oarg{type} \marg{cs name clist}
    \verb|\envref|     \oarg{type} \marg{env name}
    \verb|\envref|     \oarg{type} \parg{cs label} \marg{env name}
    \verb|\envreflist| \oarg{type} \marg{env name clist}
    \verb|\keyref|     \oarg{key path} \marg{key name}
    \verb|\keyref|     \oarg{key path} \parg{key label} \marg{key name}
    \verb|\keyreflist| \oarg{key path} \marg{key name clist}
  \end{syntax}
引用命令，环境或键。对于列表的引用，可以通过 \cs{cus@doc@refrange} 修改分隔字符。

\cs{cus@doc@csref@format} 等三个命令则是修改它们显示的格式，最多可带有一个参数。

\meta{type} 为 \keyref[doc/cmd]{type} 设置的值。
\end{function}

\begin{function}{\cus@doc@ttfont,\cus@doc@itfont}
这两个命令分别用于设置 \cuslibrary{doc} 库中使用的等宽字体和斜体。
\end{function}

\begin{function}[type=color name,module=color name]{cus/color/doc cs,
  cus/color/doc env,cus/color/doc key}
它们是颜色名，分别用于设置 \csreflist{csref,envref,keyref} 命令中链接的颜色。可以使用
\tn{colorlet} 等命令修改。
\end{function}

\begin{function}{\g_cus_doc_type_alias_prop,\g_cus_doc_module_alias_prop}
这两个 prop 分别用于设置 \opt{type} 和 \opt{module} 的实际值。
\end{function}

\section{\cuslibrary{bnf}库}

\cusmodule{bnf} 库用于排版基于 Backus-Naur Form（BNF 范式）的文法。

\begin{function}[type=environment]{latexbnf}
  \begin{syntax}
    \verb|\begin{latexbnf}|\oarg{texbnf key-val}
      ~~\verb"<"\meta{non-terminal}\verb">" \verb":" \verb"<"\meta{non-terminal}\verb">"
      ~~\verb"<"\meta{non-terminal}\verb">" \verb":" \verb|"|\meta{terminal}\verb|"|
      ~~\verb"<"\meta{non-terminal}\verb">" \verb":" \verb"<"\meta{non-terminal}\verb">" \verb"|" \verb"<"\meta{non-terminal}\verb">"
      ~~... 
    \verb|\end{latexbnf}|
  \end{syntax}
BNF 范式排版环境。

可使用 \verb|::=| 代替 \verb|:|。

当 \verb"<" 在行首时，被解释为定义一个新的句法。

在此环境中，\verb|_|、\verb|^| 相当于 \cs{lo}、\cs{hi}，可以直接在文本中使用，分别表示上下标。

排版时，既可使用这种字符标记的形式，也可使用下述的命令形式。混合使用它们也是可被接受的。

这些字符标记中的文字被正常处理。

连续使用两次 \verb|:| 可输出“{ : }”，连续使用两次 \verb"|" 可输出“{ | }”。

这些标记字符在数学模式中表示它们原本的含义。

此环境中空行被忽略了，若要显示空行，可以在此行使用 \tn{null} 或使用一个空盒子：\verb|\mbox{}|。

本环境中还可使用 \cs{V}，它相当于 \csref{Verbatimize}。
\end{function}

\begin{function}{\BNFItem,\BNFN,\BNFI,\BNFO,\BNFT}
  \begin{syntax}
    \verb|\BNFN|   \marg{non-terminal}
    \verb|\BNFN| * \marg{non-terminal}
    \verb|\BNFN| + <token> <{non-terminal}> <token>
    \verb|\BNFT|   \marg{terminal}
    \verb|\BNFT| * \marg{terminal}
    \verb|\BNFT| + <token> <{terminal}> <token>
  \end{syntax}
\cs{BNFItem} 用于标记一个句法（syntax）的开始。

\cs{BNFN} 排版非终结符。\cs{BNFT} 排版终结符。

\cs{BNFI} 表示它之前的内容被定义为它之后的内容。

\cs{BNFO} 表示“或者”。

除 \cs{BNFItem} 外，上述命令均可在正文环境中使用。

在 \envref{latexbnf} 环境中，可使用 \cs{is} 代替 \cs{BNFI}，\cs{alt} 代替 \cs{BNFO}，它们会在两侧加上空白。
\end{function}

本库还支持给非终结符加上超链接。

当加载了 \pkg{hyperref} 宏包后，右侧的非终结符将链接到对应的定义处（如果其定义存在）。

当使用字符标记时，可使用 \verb|\h<|\meta{non-terminal}\verb|>| 的形式显示使用。在定义的左侧使用时，被解释为设置该非终结符的超链接位置；在定义的右侧使用时，被解释为链接到这个非终结符的定义处。
可以显式使用 \cs{BNFanchor} 或 \cs{BNFref} 来表示上述的两种类型。

\begin{keyval}[path=texbnf]{hyper,hyper-color}
  \begin{syntax}
    hyper = <&\TTF> & false 
    hyper-color = \marg{颜色}
  \end{syntax}
\opt{hyper} 控制是否使用默认使用超链接而无需显示使用 \verb|\h|。

\opt{hyper-color} 控制超链接的颜色。未给定时，使用超链接默认的颜色。

\cs{BNFN} 超链接的使用与否也受 \opt{hyper} 选项控制。
\end{keyval}


\begin{xample}[language={[BNF]{TeX}}]
\begin{latexbnf}[hyper, hyper-color=purple]
<glue> ::= <optional signs><internal glue>
  | <dimen><stretch><shrink>
<stretch> ::= "plus"<dimen> | "plus"<fil dimen> | <optional spaces>
<shrink> ::= "minus"<dimen> | "minus"<fil dimen> | <optional spaces>
<fil dimen> ::= <optional signs><factor><fil unit><optional spaces>
<fil unit> ::= "fil" | <fil unit>"l"
<muglue> ::= <optional signs><internal muglue>
  | <mudimen><mustretch><mushrink>
<mustretch> ::= "plus"<mudimen> | "plus"<fil dimen> | <optional spaces>
<mushrink> ::= "minus"<mudimen> | "minus"<fil dimen> | <optional spaces>
\end{latexbnf}
\stopxamplecode
\xamplecode
\xampleline\par\nointerlineskip\par
\xampletext
\end{xample}

完全等价的一个写法是：
\begin{xample}[language={[BNF]{TeX}}]
\begin{latexbnf}[hyper, hyper-color=purple]
\BNFItem \BNFN{glue}\is\BNFN{optional signs}\BNFN{internal glue}
  \alt\BNFN{dimen}\BNFN{stretch}\BNFN{shrink}
\BNFItem \BNFN{stretch}\is\BNFT{plus}\BNFN{dimen}\alt\BNFT{plus}\BNFN{fil dimen}\alt\BNFN{optional spaces}
\BNFItem \BNFN{shrink}\is\BNFT{minus}\BNFN{dimen}\alt\BNFT{minus}\BNFN{fil dimen}\alt\BNFN{optional spaces}
\BNFItem \BNFN{fil dimen}\is\BNFN{optional signs}\BNFN{factor}\BNFN{fil unit}\BNFN{optional spaces}
\BNFItem \BNFN{fil unit}\is\BNFT{fil}\alt\BNFN{fil unit}\BNFT{l}
\BNFItem \BNFN{muglue}\is\BNFN{optional signs}\BNFN{internal muglue}
  \alt\BNFN{mudimen}\BNFN{mustretch}\BNFN{mushrink}
\BNFItem \BNFN{mustretch}\is\BNFT{plus}\BNFN{mudimen}\alt\BNFT{plus}\BNFN{fil dimen}\alt\BNFN{optional spaces}
\BNFItem \BNFN{mushrink}\is\BNFT{minus}\BNFN{mudimen}\alt\BNFT{minus}\BNFN{fil dimen}\alt\BNFN{optional spaces}
\end{latexbnf}
\stopxamplecode
\xamplecode \smallskip\medskip 
\end{xample}

\begin{keyval}[path=texbnf]{format,Nformat,Iformat,Oformat,Tformat,clear-all-format}
  \begin{syntax}
    format = \marg{code}
    Tformat = \marg{code} & \V\ttfamily
    clear-all-format &&
  \end{syntax}
设置格式。
\end{keyval}

\begin{keyval}[path=texbnf]{Nleft,Nright,Tleft,Tright,N,T,O,I}
  \begin{syntax}
    Nleft = \marg{code} & \V{\ensuremath{\langle}}, \ensuremath{\langle}
    Nright = \marg{code} & \V{\ensuremath{\rangle}}, \ensuremath{\rangle}
    I = \marg{code} & \V{\ensuremath{\longrightarrow}}, \ensuremath{\longrightarrow}
    O = \marg{code} & \V{\cus@mathrule}, \UseName{cus@mathrule}
  \end{syntax}
设置 \cs{BNFN}、\cs{BNFT} 左右的符号，\cs{BNFI}、\cs{BNFO} 的替换符号。
\end{keyval}

\begin{keyval}[path=texbnf]{label-prefix,label-suffix}
  \begin{syntax}
    label-prefix = \marg{前缀} & texbnf//
    label-suffix = \marg{后缀}
  \end{syntax}
当设置超链接锚点时，会写入 \tn{label}，使用 \opt{label-prefix} 和 \opt{label-suffix}
可在 \tn{label} 名中添加 \meta{前缀} 和 \meta{后缀}。使用 \cs{BNFref} 时，也需正确添加它们。
\end{keyval}


\section{\cuslibrary{ref}库}\label{sec:lib-ref}

\cusmodule{ref} 库提供交叉引用的一些额外功能。

本库改进了 \cs{IfPageOdd} 和 \cs{IfAbsPageOdd}，使得它们在任何位置都有效。

\begin{function}{\IfLabelOdd}
  \begin{syntax}
    \V\IfLabelOdd \marg{label} \marg{true} \marg{false}
  \end{syntax}
判断这个 \meta{label} 是否定义在奇数页。当 \meta{label} 不存在时使用 \meta{false}。

\meta{label} 所在页码必须以阿拉伯数字显示，否则使用 \meta{false} 分支。
\end{function}

\begin{function}{\HyperRef,\HyperLink}
  \begin{syntax}
    \V\HyperRef   \marg{label} \marg{material}
    \V\HyperRef * \marg{label} \marg{material}
    \V\HyperLink   \marg{target} \marg{material}
    \V\HyperLink * \marg{target} \marg{material}
  \end{syntax}
\pkg{hyperref} 宏包提供了 \tn{hyperref} 和 \tn{hyperlink} 命令，用于链接到 label 或 hyper target，
但是这两个命令的参数不能包含特殊的文本，如不能包含 verbatim 文本和 \tn{parbox} 等。
\cs{HyperRef} 和 \cs{HyperLink} 用于解决这个问题。

\cs{HyperRef}\marg{label}\marg{material} 和 \tn{hyperref}\oarg{label}\marg{text} 效果完全一样。
其作用是把 \meta{text} 链接到 \meta{label} 所在的那个地方，但可以包含特殊文本，仅能在特殊的位置断行。

\cs{HyperLink}\marg{target}\marg{text} 和 \tn{hyperlink}\oarg{target}\marg{text}
完全一样，其作用是把 \meta{text} 链接到 \meta{target} 所在的那个地方，但可以包含特殊文本，仅能在特殊的位置断行。

它们还有带星号的用法。功能相同，但 \meta{material} 中可以有特殊的文本，也可以正常换行。

需注意比如虽然 \meta{material} 可以包含特殊文本，如 \env{parbox} 等，但其本身并不能作为其它命令的参数。

另见 \csref{cus_ref_label_box:nn}、\csref{cus_ref_target_box:nn} 和 
\csref{cus_ref_label_shbox:nn}、\csref{cus_ref_target_shbox:nn}。
\end{function}

除了上面所说的简单的用法，这两个命令还支持用可选参数设置 \meta{material} 的宽度、高度及对齐方式。
\begin{function}[label={HyperRef2,HyperLink2}]{\HyperRef,\HyperLink}
  \begin{syntax}
    \V\HyperRef   \marg{label} \oarg{width} \meta{material}
    \V\HyperRef * \marg{label} \oarg{width} \meta{material}
    \V\HyperRef   \marg{label} \oarg{width} \oarg{vpos} \oarg{height} \oarg{inner pos} \meta{material}
    \V\HyperRef * \marg{label} \oarg{width} \oarg{vpos} \oarg{height} \oarg{inner pos} \meta{material}
  \end{syntax}

当不带星号使用时，相当于把 \meta{material} 放在 \env{minipage} 中，\meta{material} 的宽度设置为 \meta{width}，但可以包含特殊文本。
当带星号使用时，相当于把 \meta{material} 放在 \env{varwidth} 中，\meta{material} 的\emph{最大}宽度为 \meta{width}，也可以包含特殊文本。

它们的可选参数正如 \tn{parbox} 的可选参数那样：
\begin{itemize}
\item \meta{vpos} 表示垂直位置，可选值为 \texttt{b}、\texttt{c}、\texttt{t}，分别表示对齐底部基线、居中对齐、对齐顶部基线，默认为居中对齐；
\item \meta{height} 表示盒子的高度，如果不设置，则为盒子的自然高度，
\item \meta{inner pos} 表示如果设置的 \meta{height} 过大时，盒子的内容在盒子内的垂直位置，可选值为 \texttt{b}、\texttt{c}、\texttt{t}、\texttt{s}，分别表示置于盒子底部、居中、置于顶部、垂直分散对齐，默认为垂直分散对齐。
\end{itemize}

另见 \cs{collectn_minipage:Nnnnnnw}、\cs{collectn_varwidth:Nnnnnnw}。
\end{function}

虽然这两个命令不能配置 \meta{material}，但 \cuslibrary{box} 库提供了 \csref{fparbox} 和 \csref{fvarbox} 命令可以设置盒子外框，见\cref{sec:fparbox-fvarbox}及\cref{eg:hyperref-fparbox}。


\section{\cuslibrary{box}库}\label{sec:lib-box}

\cusmodule{box} 库提供额外的一些盒子。

\subsection{\env{paracol}环境}

\pkg{paracol} 提供了另一种多栏盒子。它可以控制文字出现在某栏，也可以手动对齐各栏的文字，常用于排版多语言对照文本。

\begin{function}{\startparacol,\stopparacol}
  \begin{syntax}
    \V\startparacol \oarg{paracol keyval}
    ~~...
    \V\stopparacol 
  \end{syntax}
使用 \pkg{paracol} 宏包排版多栏文字。
\end{function}

\begin{function}[module=paracol]{\switchcolumn}
  \begin{syntax}
    \V\switchcolumn 
    \V\switchcolumn *
    \V\switchcolumn \oarg{col}
    \V\switchcolumn \oarg{col} * 
    \V\switchcolumn \oarg{col} * \oarg{heading}
  \end{syntax}
切换至第 \meta{col} 栏，栏以 0 开始计数。若不给出 \meta{col} 则切换至下一栏。

如果 \texttt{*} 给出，则先构建先前的文字，将它们对齐，然后接下来的文字的对齐位置为这些已经对齐了的文字的底部。

如果 \meta{heading} 给出，则作为通栏文字插入。
\end{function}

\begin{function}[module=paracol]{\thecolumn}
当前栏数，以 0 开始计数。
\end{function}

\begin{keyval}[path=paracol]{cols,numleft,paired}
  \begin{syntax}
    cols    = \marg{总栏数}
    numleft = \marg{在左页的栏数}
    paired  = \TTF & true 
  \end{syntax}
\meta{cols} 设置多栏的总栏数。\env{paracol} 环境支持多栏分布在左右两页，通过 \meta{numleft} 设置在左侧页的栏数。在此情况下，\meta{paired} 用于设置这左右两页的页码是否相同。
\end{keyval}

\begin{keyval}[path=paracol]{heading}
  \begin{syntax}
    heading = \marg{text}
  \end{syntax}
在多栏文本前插入通栏文字 \meta{text}。
\end{keyval}

\begin{keyval}[path=paracol]{column-ratio,column-ratio-left,column-ratio-right}
  \begin{syntax}
    column-ratio       = \marg{r_0, r_1, $\cdots$, r_k} \oarg{r_0', r_1', $\cdots$, r_k'}
    column-ratio-left  = \marg{r_0, r_1, $\cdots$, r_k}
    column-ratio-right = \marg{r_0', r_1', $\cdots$, r_k'}
  \end{syntax}
设置每栏宽度的占比。\meta{column-ratio} 的可选参数和 \meta{column-ratio-right} 设置的是右侧页的栏。

每栏的实际宽度为 $r_i\times\bigl(\tn{textwidth}-(n-1)\tn{columnsep}\bigr)$。未给出的栏的宽度为剩余的宽度除以剩余的栏数。

其作用类似于 \tn{columnratio}。

每个环境开始时，它总被重置。
\end{keyval}

\begin{keyval}[path=paracol]{column-width,column-width-left,column-width-right}
  \begin{syntax}
    column-width       = \marg{s_0, s_1, $\cdots$, s_k} \oarg{s_0', s_1', $\cdots$, s_k'}
    column-width-left  = \marg{s_0, s_1, $\cdots$, s_k}
    column-width-right = \marg{s_0', s_1', $\cdots$, s_k'}
  \end{syntax}
设置每栏的宽度和间距。\meta{column-width} 的可选参数和 \meta{column-width-right} 设置的是右侧页的栏。

$s_i$ 的形式为 $\hat{w}_i$ 或 $\hat{w}_i \texttt{/} \hat{g}_i$，这里 $\hat{w}_i$、$\hat{g}_i$ 为一个 glue 或 dim，或为空表示 $\hat{w}_i$ \verb|=\fill|，$\hat{g}_i$ \verb|=\columnsep|。未给出的假定为空。

如果给出的总宽度大于 \tn{textwidth}，则每个宽度按比例缩放，使得总宽度为 \tn{textwidth}。

其作用类似于 \tn{setcolumnwidth}。

每个环境开始时，它总被重置。
\end{keyval}

例如，假设 \verb|\textwidth=360pt|，\verb|\columnsep=| $S$ \verb|=20pt|，则

\begin{pagecenterbox}
\def\|{\verb|}\small\arraycolsep0pt\def\arraystretch{1.1}
$\begin{array}[b]{l|ccccc}
s_0,s_1,s_2&w_0&g_0&w_1&g_1&w_2\rlap{ (in \texttt{pt})}\\\hline
\|50pt/20pt,100pt/40pt,150pt|&50&20&100&40&150\\
\|50pt,100pt/2\columnsep,150pt|&50&S&
                               100&2S&150\\
\|50pt/\fill,100pt/2\fill,150pt|&50&(1/3)\cdot60&100&(2/3)\cdot60&150\\
\|,2\fill/2\columnsep,3\fill|&\ (1/6)\cdot300&S&
                             (2/6)\cdot300&2S&
                             (3/6)\cdot300\\
\|50pt/20pt,50pt plus 1fil/40pt,50pt plus 2fil |&
                             50&20&50+(1/3)\cdot150&40&
                             50+(2/3)\cdot150\\
\|5pt/2pt,10pt/4pt,15pt|&10\cdot5&10\cdot2&10\cdot10&10\cdot4&
                        10\cdot15\\
\|100pt/40pt,200pt/80pt,300pt|&0.5\cdot100&0.5\cdot40&
                              0.5\cdot200&0.5\cdot80&
                              0.5\cdot300
\end{array}$
\end{pagecenterbox}

\begin{function}[module=paracol]{\columnwidth}
保存了当前栏宽度的 dim 寄存器。
\end{function}

\begin{keyval}[path=paracol]{twosided}
  \begin{syntax}
    twosided = <&page|p|column|c|margin|m|background|b|all|none> & all 
  \end{syntax}
使用 \texttt{twoside} 排版特性。在奇偶页输出不同的效果。

\begin{description}[nosep]
  \item[\texttt{page|p}] 正如 \texttt{twoside} 文档类选项控制的那样。
  \item[\texttt{column|c}] 在偶数页逆序输出各栏。
  \item[\texttt{margin|m}] 切换边注的位置。
  \item[\texttt{background|b}] 切换背景。
  \item[\texttt{all}] 设置上述为真。
  \item[\texttt{none}] 设置上述为假。注意若要单独设置某个选项，需要先使用 \opt{none} 将它们都设置为假。
\end{description}
\end{keyval}

\begin{keyval}[path=paracol]{marginpar-threshold,marginpar-threshold-left,marginpar-threshold-right}
  \begin{syntax}
    marginpar-threshold       = \marg{k} \oarg{k'}
    marginpar-threshold-left  = \marg{k}
    marginpar-threshold-right = \marg{k'}
  \end{syntax}
设置前 $k$ 栏的边注放在左侧。

注意，边注的位置还受到 \opt{twosided} 选项中的 \opt{margin} 的控制和 \tn{reverse\-marginpar} 的控制。
\end{keyval}

\begin{keyval}[path=paracol]{counter-global,counter-local}
  \begin{syntax}
    counter-global = \marg{counter clist} | *
    counter-local  = \marg{counter clist}
  \end{syntax}
默认情况下，除了 \texttt{page} 计数器外，其它计数器的值的改变仅作用于某一栏。可以使用 \opt{counter-global} 设置对各栏可见。\opt{counter-local} 的效果则相反。

设置 \opt{counter-global} 为 \texttt{*} 时，相当于设置所有计数器。

它们的设置是全局的。相当于 \tn{globalcounter}、\tn{localcounter} 命令。
\end{keyval}

\begin{function}[module=paracol]{\definethecounter}
  \begin{syntax}
    \V\definethecounter \marg{counter} \marg{col} \marg{rep}
  \end{syntax}
  将第 \meta{col} 栏的 \tn{the\meta{counter}} 修改为 \meta{rep}。
\end{function}

\begin{function}[module=paracol]{\synccounter,\syncallcounter}
  \begin{syntax}
    \V\synccounter \marg{counter}
    \V\syncallcounter 
  \end{syntax}
  将计数器 \meta{counter} （或所有计数器）在此栏的值同步到其它栏。
\end{function}

\begin{keyval}[path=paracol]{column-sep-rule}
  \begin{syntax}
    column-sep-rule = \marg{长度}
  \end{syntax}
  设置栏间分割线的宽度。
\end{keyval}

\begin{keyval}[path=paracol]{before,before+,after,after+}
  \begin{syntax}
    before  = \marg{code}
    before+ = \marg{code}
  \end{syntax}
  设置在环境开始或结束时要执行的代码。
\end{keyval}

\begin{keyval}[path=paracol]{preamble}
  \begin{syntax}
    preamble       \;= \marg{code}
    preamble \oarg{col} = \marg{code}
  \end{syntax}
设置第 \meta{col} 栏开始时执行的代码。\meta{col} 为 $-1$ 表示通栏文字执行前执行的代码。如果 \meta{col} 未给出则为 $-1$。
\end{keyval}

\begin{function}[module=paracol]{\columncolor,\normalcolumncolor,\colseprulecolor,\normalcolseprulecolor}
  \begin{syntax}
    \V\columncolor \oarg{mode} \marg{color} \oarg{col}
    \V\normalcolumncolor \oarg{col}
    \V\colseprulecolor \oarg{mode} \marg{color} \oarg{col}
    \V\normalcolseprulecolor \oarg{col}
  \end{syntax}
  设置每栏文字的颜色即栏间竖线的颜色。\cs{normalcolumncolor}、\cs{normalcolsep\-rulecolor} 用于恢复为原始颜色。

  如果在环境外使用，则 \meta{col} 未给出时设置的是第 0 栏的颜色；在环境内使用，则设置的是当前栏的颜色。

  设置是全局的。
\end{function}

\begin{keyval}[path=paracol]{contents}
  \begin{syntax}
    contents = \marg{file} \marg{col}
  \end{syntax}
  将第 \meta{col} 栏的章节添加到目录文件 \meta{file}。\meta{file} 只能是 \texttt{toc}、\texttt{lof}、\texttt{lot} 之一。

  设置是全局的。
\end{keyval}

关于 \pkg{paracol} 宏包的其它用法见 \pkgdoc{paracol}。

\subsection{\texttt{multicolumns/framed=lfbox}}\label{sec:multicolumns/framed=lfbox}

本库提供为多栏文字的外框提供了了 \opt{lfbox} 可选值。表示用 \pkg{longfbox} 宏包的 
\tn{lfbox} 作为盒子外框。\keyref[multicolumns]{framed-options} 可以使用 \pkg{longfbox} 
宏包提供的选项。

\subsection{\cs[no-index]{fparbox} 和 \cs[no-index]{fvarbox}，可设置外框的命令}
\label{sec:fparbox-fvarbox}

\pkg{longfbox} 宏包提供了使用 CSS 属性名来设置盒子外框的命令 \tn{lfbox} 和环境 \env{longfbox}，但 \tn{lfbox} 只能包含水平模式的内容，这里提供了 \cs{fparbox} 和 \cs{fvarbox} 以补全这一缺点。

\begin{function}{\fparbox,\fvarbox}
  \begin{syntax}
    \V\fparbox \marg{width} \marg{material}
    \V\fparbox \oarg{vpos} \oarg{height} \oarg{inner pos} \marg{width} \oarg{longfbox options} \marg{material}
    \V\fvarbox \marg{width} \marg{material}
    \V\fvarbox \oarg{vpos} \oarg{height} \oarg{inner pos} \marg{width} \oarg{longfbox options} \marg{material}
  \end{syntax}

\cs{fparbox} 把 \meta{material} 封装进 \env{minipage} 中，\cs{fvarbox} 把 \meta{material} 封装进 \env{varwidth} 中。

\meta{vpos}、\meta{height}、\meta{inner pos}、\meta{width}、\meta{material} 选项的含义前面已提过多次，如在 \csref(HyperLink2){HyperRef} 命令的说明中。\meta{longfbox options} 为 \pkg{longfbox} 中可用的键值选项。
\end{function}

\begin{xample}
\hypersetup{linkcolor=red} 链接到
\HyperRef{sec:fparbox-fvarbox}{\fparbox[t]{3cm}
  [border-color={}]{可以分段的\par \verb|\lfbox|}}
\HyperRef{sec:fparbox-fvarbox}{\fvarbox[c]{3cm}
  [border-color={}]{可以分段的\par \verb|\lfbox|}}
\stopxamplecode
\label{eg:hyperref-fparbox}
\xampleprint 
\end{xample}

\section{\cuslibrary{math}库}

\begin{function}{\delsizel,\delsizem,\delsizer,\bigsizel,\bigsizem,\bigsizer}
  \begin{syntax}
    \V\delsizel \marg{real} \meta{left}
    \V\delsizem \marg{real} \meta{middle}
    \V\delsizer \marg{real} \meta{right}
  \end{syntax}
正如 \tn{bigl}、\tn{bigm} 和 \tn{bigr} 那样，但如上几个命令可以设置括号的大小。

\tn[no-index]{delsize..} 设置的 \meta{real} 为数学模式下左括号 \texttt{(} 高度的倍数。
\tn[no-index]{bigsize..} 设置的 \meta{real} 为数学模式下左括号 \texttt{(} 高度的倍数的 1.2 倍。

例如，\tn{bigl} 相当于 \verb|\bigsizel{1}|。
\end{function}


\section{\cuslibrary{counter}库}

本库定义了与计数器相关的一些命令。

\begin{function}[rEXP]{\ensuretwodigits,\ensurethreedigits,\ensurefourdigits}
  \begin{syntax}
    \V\ensuretwodigits \marg{计数器}
  \end{syntax}
类似于 \tn{arabic}，只不过确保它至少输出 2 （或3、4）个数，不足的补 0。
\end{function}

\begin{function}{\MakePerPage}
  \begin{syntax}
    \V\MakePerPage                 \!\marg{counter clist}
    \V\MakePerPage   \oarg{initial value} \marg{counter clist}
    \V\MakePerPage *               \!\marg{int/count clist}
    \V\MakePerPage * \oarg{initial value} \marg{int/count clist}
  \end{syntax}
不带星号的命令的作用为，在新的一页重设 \meta{counter clist} 中的计数器为 
\meta{initial value}，这个初值默认为 0。

带星号的命令的作用为，在新的一页重设 \meta{int/count clist} 中的寄存器为
\meta{initial value}，这个初值默认为 0。

\meta{counter clist} 中的计数器不是必须用 \tn{newcounter} 来定义。
\end{function}

\begin{function}{\IfIsCounterTF}
  \begin{syntax}
    \V\IfIsCounterTF \marg{tl} \marg{true} \marg{false}
  \end{syntax}
判断 \meta{tl} 是不是计数器，必须是使用 \tn{newcounter} 定义的。

\begin{texnote}
一个计数器并不一定直接由 \tn{newcounter} 定义，也可能由其它命令间接调用 \tn{newcounter}。

只要 \meta{tl} 在 \tn{cl@@ckpt} 中，则结果为 \meta{true}，否则为 \meta{false}。
\end{texnote}
\end{function}

\begin{function}{\RecordTotalCounters,\IfRecordTotalCounterTF}
  \begin{syntax}
    \V\RecordTotalCounters \marg{counter clist}
    \V\IfRecordTotalCounterTF \marg{counter} \marg{true} \marg{false}
  \end{syntax}
记录计数器的总次数。可使用 \cs{thetotal\meta{counter}s} 来获取这个值。
当加载了 \pkg{xspace} 宏包后，\cs{thetotal\meta{counter}x} 会在 \cs{thetotal\meta{counter}s} 后加上 \tn{xspace}。

\cs{thetotal\meta{counter}s} 一般用于正文中。只能在加载了 \texttt{.aux} 文件后才能使用。
即可以在 \hook{begindocument}（或 \cs{AtBeginDocument}）、\hook{begindocument/end} 钩子中使用。
\end{function}


\section{\cuslibrary{pdf}库}

本库提供一些与 PDF 文件的特性相关的功能。它们只能在使用了 
\cs[break-at-any]{DocumentMetadata} 之后使用。

默认情况下，在 \TeX 中直接导入 PDF 文件会丢失该 PDF 的超链接信息，使用本库可以解决此问题。
相较于直接使用 \pkg{newpax} 宏包，若导入的 PDF 文件中有很多超链接，
本库提供的 \cs{includegraphicspax} 速度会快很多。

\begin{keyval}[path=pdf]{pax,pax+}
  \begin{syntax}
    pax = \marg{pdf file clist}
  \end{syntax}
为了能够保留导入的 PDF 文件的超链接信息，需要为这些 PDF 文件预先生成一些必要的辅助文件。
本选项用于设置需要保留超链接信息的那些 PDF 文件。

如果使用 \LuaLaTeX 则可以自动生成。其它引擎需要开启 \texttt{shell-escape} 功能。

仅在导言区设置才有效。
\end{keyval}

\begin{keyval}[path=pdf]{graphics-path}
  \begin{syntax}
    graphics-path = \marg{path clist}
  \end{syntax}
在查找需要保留超链接的 PDF 文件时，设置查找路径。
如果没有设置，则使用 \tn{setgraphicspath} 设置的。
\end{keyval}

\begin{function}{\includegraphicspax}
  \begin{syntax}
    \V\includegraphicspax * \oarg{graphics options} \marg{filename}
  \end{syntax}
用此命令导入 PDF 文件可以保留超链接。需编译两次。
\end{function}

\begin{keyval}[path=pdf]{redefine}
  \begin{syntax}
    redefine = <&\TTF> & false
  \end{syntax}
是否将 \tn{includegraphics} 重定义为 \tn{includegraphicspax}。
\end{keyval}


\chapter{可单独加载的宏包}

\section{\pkg{collectn}}\label{sec:collectn}

\begin{function}{\collectn_verb:Nnw,\l_collectn_long_verbatim_bool}
  \begin{syntax}
    \V*|\collectn_verbatim:Nnw| \meta{tl} \marg{code} \meta{token} \meta{tokens} \meta{token}
    \V*|\collectn_verbatim:Nnw| \meta{tl} \marg{code} \marg{balanced tokens}
  \end{syntax}
向后以 verbatim 的形式收集内容，将其保存至 \meta{tl} 中，再执行 \meta{code}。
\meta{token} 或 \meta{\meta{balanced tokens}} 不能在命令的参数里。

\cs{l_collectn_long_verbatim_bool} 控制是否接受长的 verbatim 文本（包含 \tn{par} 的）。

在 \meta{tokens} 或 \meta{balanced tokens} 中，字母的类别码不能为 1、2、10。

结果 \meta{tl} 包含的字符其类别码有三种，为 10、12、13
（在 \pupTeX 引擎下，仅 Unicode 编码小于等于 255 的字符有此特性），
且 \texttt{\^{}\^{}M} 的类别码为 13。
\end{function}

\begin{function}{\collectn_varwidth:nnw,\collectn_varwidth_end:}
  \begin{syntax}
    \V*|\collectn_varwidth:nnw| \marg{vertical pos} \marg{maximum width} 
    ~~~~\meta{contents} \V*|\collectn_varwidth_end:|
  \end{syntax}
\meta{contents} 是可变宽的，最大宽度为 \meta{maximum width}；基线的位置为 \meta{vertical pos}，可选值为 \texttt{b}、\texttt{c}、\texttt{t}，分别表示对齐底部基线、居中对齐、对齐顶部基线。

该命令是对 \env{varwidth} 环境的封装。
\end{function}

\begin{function}{\collectn_set_varwidth:Nnnw,\collectn_set_varwidth_end:,
  \collectn_gset_varwidth:Nnnw,\collectn_gset_varwidth_end:
}
  \begin{syntax}
    \V*|\collectn_set_varwidth:Nnnw| \meta{box} \marg{vertical pos} \marg{maximum width}
    ~~~~\meta{contents} \V*|\collectn_set_varwidth_end:|
  \end{syntax}
设置 \meta{box} 为一个包含 \meta{contents} 的最大宽度为 \meta{maximum width} 的水平盒子。
\end{function}

\begin{function}{\collectn_set_vbox_width:Nnw,\collectn_set_vbox_width_end:,
  \collectn_set_vbox_varwidth:Nnw,\collectn_set_vbox_varwidth_end:,
  \collectn_gset_vbox_width:Nnw,\collectn_gset_vbox_width_end:,
  \collectn_gset_vbox_varwidth:Nnw,\collectn_gset_vbox_varwidth_end:
}
  \begin{syntax}
    \V*|\collectn_set_vbox_width:Nnw| \meta{box} \marg{width} 
    ~~~~\meta{content} \V*|\collectn_set_vbox_width_end:|
  \end{syntax}

\cs{collectn_set_vbox_width:Nnw} 与 \cs{vbox_set:Nw} 类似，把 \meta{content} 保存到 vbox \meta{box} 中，该 \meta{box} 的宽度为 \meta{width}。它和 \env{minipage} 环境相似。

\cs{collectn_set_vbox_varwidth:Nnw} 则是设置 \meta{content} 的最大宽度为 \meta{width}。
它和 \env{varwidth} 环境相似。

这 \meta{box} 可以使用 \tn{vsplit} 或 \cs{vbox_set_split_to_ht:NNn} 来分割。
\end{function}

% peek or pick or collect?
\begin{function}{\collectn_box:Nnw}
  \begin{syntax}
    \V*|\collectn_box:NNnw| \meta{box} \meta{primitive box cs} \marg{code} \meta{material}
  \end{syntax}
先将 \meta{material} 保存到 \meta{box} 中，此盒子为 \meta{primitive box cs}，
可为 \tn{hbox}、\tn{vbox}、\tn{vtop} 之一。之后再使用 \meta{code} 处理。

\meta{material} 可以是 \texttt\{\BNFN{horizontal/vertical mode material}\texttt\}，也可以有 \BNFN{box specification}。左右括号可以是隐式的。

这种方式与 \cs{peek_charcode_remove:NTF} 类似。另见 \pkg{collectbox} 宏包。

\meta{code} 与 \cs{collectn_box:Nnw} 在同一个组中执行。

注意：\cs{hbox:n}、\cs{vbox:n}、\cs{vbox_top:n} 并不分别等于 \tn{hbox}、\tn{vbox}、\tn{vtop}。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cs_set:Npn \myfbox 
  {
    \collectn_box:NNnw \l_tmpa_box \tex_hbox:D 
      { \fbox { \box_use_drop:N \l_tmpa_box } }
  }
\ExplSyntaxOff
\myfbox{\verb|\myfbox| 与 \verb|\fbox|}
\myfbox{\parbox{3cm}{可分段的\par fbox}}
\stopxamplecode 
\xampleprint 
\end{xample}
另见\cref{eg:myfbox-cmd}。

\begin{function}{\collectn_value:Nnw}
  \begin{syntax}
    \V*|\collectn_value:Nnw| \meta{register} \marg{code} \meta{value}
  \end{syntax}
将 \meta{value} 保存到 \meta{register} 中，再使用 \meta{code} 处理。
\meta{value} 必须确实可以保存到 \meta{register} 中。

\meta{code} 与此命令在同一个组中执行。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cs_set:Npn \showintval 
  { \collectn_value:Nnw \l_tmpa_int { 值为 $ \int_use:N \l_tmpa_int $ } }
\ExplSyntaxOff
\showintval 3
\showintval -310
\showintval "3FA
\showintval \shellescape
\stopxamplecode 
\xampleprint 
\end{xample}

\begin{function}{\collectn_width:Nnnw,\collectn_minipage:Nnnw,\collectn_varwidth:Nnnw}
  \begin{syntax}
    \V*|\collectn_width:Nnnw| \meta{box} \marg{code} \marg{width} \meta{material}
  \end{syntax}
先使用类似于 \env{minipage}（\env{varwidth}）的处理方式处理 \meta{material}，
然后将它保存到 vbox \meta{box} 中，再使用 \meta{code} 处理。
这 \meta{box} 的宽度（或最大宽度）为 \meta{width}。

\meta{material} 可以是 \texttt\{\BNFN{vertical mode material}\texttt\}，正如 \tn{vbox}
\texttt\{\BNFN{vertical mode material}\texttt\} 那样。左右括号可以是隐式的。

\meta{code} 与这些命令在同一个组中执行。

\cs{collectn_minipage:Nnnnw} 是 \cs{collectn_width:Nnnnw} 的另一个名字。

保存的垂直盒子可以用 \tn{vsplit} 或 \cs{vbox_set_split_to_ht:NNn} 来分割。
\end{function}

\begin{function}{\collectn_width:Nnnnw,\collectn_minipage:Nnnnw,
  \collectn_varwidth:Nnnnw}
  \begin{syntax}
    \V*|\collectn_width:Nnnnw| \meta{box} \marg{code} \marg{vpos} \marg{width} \meta{material}
  \end{syntax}
先使用类似于 \env{minipage}（\env{varwidth}）的处理方式处理 \meta{material}，然后将它保存到 hbox \meta{box} 中，再使用 \meta{code} 处理。这 \meta{box} 的宽度为 \meta{width}，基线的位置为 \meta{vpos}，可选值为 \texttt{b}、\texttt{c}、\texttt{t}，分别表示底部基线、居中、顶部基线，默认为底部基线。

\meta{material} 可以是 \texttt\{\BNFN{vertical mode material}\texttt\}，正如 \tn{vbox}
\texttt\{\BNFN{vertical mode material}\texttt\} 那样。左右括号可以是隐式的。

\meta{code} 与这些命令在同一个组中执行。

\cs{collectn_minipage:Nnnnw} 是 \cs{collectn_width:Nnnnw} 的另一个名字。
\end{function}

\begin{xample}
\ExplSyntaxOn
\cs_set:Npn \myparfbox #1#2
  { 
    \collectn_width:Nnnnw \l_tmpa_box 
      { \fbox { \box_use_drop:N \l_tmpa_box } } {#1} {#2}
  }
\cs_set:Npn \myvarfbox #1#2
  {
    \collectn_varwidth:Nnnnw \l_tmpa_box
      { \fbox { \box_use_drop:N \l_tmpa_box } } {#1} {#2}
  }
\ExplSyntaxOff
\myparfbox{t}{5cm}{一个可以包含 \verb|\verb| 的\par 
  定宽 \verb|\fbox|}
\myvarfbox{b}{5cm}{一个可以包含 \verb|\verb| 的\par 
  变宽 \verb|\fbox|}
\stopxamplecode 
\xampleprint 
\end{xample}

\begin{function}{\collectn_width:Nnnnnnw,\collectn_minipage:Nnnnnnw,
  \collectn_varwidth:Nnnnnnw}
  \begin{syntax}
    \V*|\collectn_width:Nnnnnnw| \meta{box} \marg{code} 
    ~~~~\marg{vpos} \marg{height} \marg{inner pos} \marg{width} \meta{material}
  \end{syntax}
前述命令的完整形式。

\begin{itemize}
\item \meta{vpos} 表示垂直位置，可选值为 \texttt{b}、\texttt{c}、\texttt{t}，分别表示对齐底部基线、居中对齐、对齐顶部基线，默认为对齐底部基线；
\item \meta{height} 表示盒子的高度，如果不设置，则为盒子的自然高度，
\item \meta{inner pos} 表示如果设置的 \meta{height} 过大时，盒子的内容在盒子内的垂直位置，可选值为 \texttt{b}、\texttt{c}、\texttt{t}、\texttt{s}，分别表示置于盒子底部、居中、置于顶部、垂直分散对齐，默认为垂直分散对齐。
\end{itemize}

\meta{material} 可以是 \texttt\{\BNFN{vertical mode material}\texttt\}，正如 \tn{vbox}
\texttt\{\BNFN{vertical mode material}\texttt\} 那样。左右括号可以是隐式的。

\meta{code} 与这些命令在同一个组中执行。
\end{function}

\begin{function}{\collectn_hbox_auto:Nnnw,\collectn_width_auto:Nnnw,
  \collectn_minipage_auto:Nnnw,\collectn_varwidth_auto:Nnnw}
  \begin{syntax}
    \V*|\collectn_hbox_auto:Nnnw| \meta{box} \marg{code} \marg{spec} \marg{content}
  \end{syntax}
根据 \meta{spec} 自动使用合适的命令。

\cs{collectn_hbox_auto:Nnnw} 的 \meta{spec} 为 \tn{makebox} 的前两个参数：
\oarg{width}\oarg{pos}。

其它三个命令的 \meta{spec} 为 \tn{parbox} 的前四个参数：
\oarg{vpos}\oarg{height}\oarg{inner pos}\marg{width}。
仅当只有 \meta{width} 给出时，所保存的盒子为垂直盒子，且可以使用 \tn{vsplit}
或 \cs{vbox_set_split_to_ht:NNn} 来分割该盒子。
\end{function}

\begin{function}[TF]{\collectn_if_in:n}
  \begin{syntax}
    \V*|\collectn_if_in:nTF| \marg{token list} \marg{true code} \marg{false code}
  \end{syntax}
判断下一个记号是否在给定的 \meta{token list} 中。

无法判断显式或隐式的左、右括号和空格。
\end{function}

\begin{function}{\collectn_delimited_inline:NNn,\collectn_delimited_variable:NNNn}
  \begin{syntax}
    \V*|\collectn_delimited_inline:NNn| \meta{token_1} \meta{token_2} \marg{inline code}
    \V*|\collectn_delimited_variable:NNNn| \meta{token_1} \meta{token_2} \meta{tl} \marg{code}
  \end{syntax}
获取接下来的以 \meta{token_1} 和 \meta{token_2} 定界的参数。
若不存在，则为 \cs{q_no_value}。\meta{inline code} 可以用 \verb|#1| 获取这个参数，
\meta{tl} 用于保存这个参数。
\end{function}

\begin{function}{\collectn_delimited_any_inline:nn}
  \begin{syntax}
    \V*|\collectn_delimited_any_inline:nn| \marg{paired tokens} \marg{inline code}
  \end{syntax}
判断接下来的定界的参数的定界符是否在 \meta{paried tokens} 之中。
\meta{inline code} 可使用 2 个参数，第一个为此定界符在 \meta{paired tokens} 中的位置，
第二个为它的值。若不存在，则第一个值为 0，第二个值为 \cs{q_no_value}。
\end{function}

\begin{function}{\collectn_delimited_all_inline:nn}
  \begin{syntax}
    \V*|\collectn_delimited_all_inline:nn| \marg{paried tokens} \marg{inline code}
  \end{syntax}
对于 \meta{paried tokens} 给出的定界符对，逐一考察是否接下来的参数以此定界符对定界。
\end{function}

\begin{function}{\l_collectn_delimited_strip_bool}
用于控制上面四个命令在获取定界的参数时，若其参数整个用 \verb|{}| 包裹，是否移除此 \verb|{}|。
\end{function}

\begin{xample}
\ExplSyntaxOn \ttfamily
\bool_set_false:N \l_collectn_delimited_strip_bool
\collectn_delimited_inline:NNn [ ] { \tl_to_str:n { |#1| } } [{bracket}] ;
\collectn_delimited_inline:NNn [ ] { \tl_to_str:n { |#1| } } \scan_stop: ;
\par
\collectn_delimited_any_inline:nn { []() } { \tl_to_str:n { |#1,#2| } }
  [[bracket]] ;
\collectn_delimited_any_inline:nn { []() } { \tl_to_str:n { |#1,#2| } }
  ({brace}) ;
\par
\collectn_delimited_all_inline:nn { []() } { \tl_to_str:n { |#1| } }
  (brace) [[bracket]] ;
\collectn_delimited_all_inline:nn { []() } { \tl_to_str:n { |#1| } }
  ({brace}) ;
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\collectn_lohi:Nnw,\collectn_lohis:Nnw}
  \begin{syntax}
    \V*|\collectn_lohi:Nnw| \meta{tl} \marg{code} \meta{content}
  \end{syntax}
判断接下来的一些字符是否是数学上下标。
\cs{collectn_lohi:Nnw} 获取一组数学上下标（即数学下标和上标每个最多获取一个）；
\cs{collectn_lohis:Nnw} 可获取任意多个上下标。把这些上下标存储到 \meta{tl} 里，
然后执行 \meta{code}。

\meta{tl} 由形如 
\verb|{^|\marg{text}\verb|}| 或 \verb|{_|\marg{text}\verb|}| 的项组成。
其中 \verb|^| 和 \verb|_| 的类别码分别为 7 和 8。

它们会自动展开后面的内容，并且自动移除空格和 \tn{relax}。

它们只会匹配类别码为 7 和 8 的显式或隐式字符。若加载了 \pkg{underscore} 宏包，则 \texttt{\_}
的类别码会修改为 13，在文本模式下无法匹配，但在数学模式下可以。
\end{function}

\begin{xample}
\ExplSyntaxOn
\collectn_lohi:Nnw \l_tmpa_tl { \tl_to_str:N \l_tmpa_tl }
  \c_math_subscript_token \scan_stop: { a } ^ { b }
\par 
\collectn_lohis:Nnw \l_tmpa_tl { \tl_to_str:N \l_tmpa_tl }
  \scan_stop: \c_math_superscript_token \scan_stop: { a }
  \scan_stop: \sb { b } \sp { c } ^{ d }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}[TF]{\collectn_scan_keyword:n,\collectn_scan_keywordcs:n}
  \begin{syntax}
    \V*|\collectn_scan_keyword:nTF| \marg{keyword} \marg{true} \marg{false}
  \end{syntax}
判断接下来的一些字符是否匹配 \meta{keyword}，若匹配则移除它们。
为了检测后面的文字是否匹配 \meta{keyword}，它会自动\emph{展开}后面的命令。

\meta{keyword} 被转为类别码为 12 的记号列（包括空格），两端的空格\emph{不会}被移除。
\cs{collectn_scan_keyword:n} 忽略大小写，\cs{collectn_scan_keywordcs:n} 不忽略大小写。
\end{function}

\begin{function}{\collectn_set_keyword:Nn,
  \collectn_set_keyword:No,\collectn_set_keyword:NV,
  \collectn_set_keyword:Nv,\collectn_set_keyword:Ne,
  \collectn_set_keyword:cn,
  \collectn_set_keyword:co,\collectn_set_keyword:cV,
  \collectn_set_keyword:cv,\collectn_set_keyword:ce
}
  \begin{syntax}
    \V*|\collectn_set_keyword:Nn| \meta{str} \marg{keyword}
  \end{syntax}
把 \meta{keyword} 处理成可供使用的记号列，然后将其保存至 \meta{str}。

先对 \meta{keyword} 执行 \cs{tl_to_str:n}，然后把所有空格替换为类别码为 12 的空格。
\end{function}

\begin{function}[TF]{\collectn_scan_keyword:N, \collectn_scan_keyword:c,
  \collectn_scan_keywordcs:N, \collectn_scan_keywordcs:c}
  \begin{syntax}
    \V*|\collectn_scan_keyword:NTF| \meta{keyword str} \marg{true} \marg{false}
  \end{syntax}
判断接下来的一些字符是否匹配 \meta{keyword}，若匹配则移除它们。
为了检测后面的文字是否匹配 \meta{keyword}，它会自动\emph{展开}后面的命令。

\meta{keyword str} 需用 \cs{collectn_set_keyword:Nn} 设置。
\end{function}

\begin{function}[TF]{\collectn_scan_keywords:n,\collectn_scan_keywordscs:n}
  \begin{syntax}
    \V*|\collectn_scan_keywords:nTF| \marg{keyword clist} \marg{true} \marg{false}
  \end{syntax}
判断接下来的一些字符是否匹配 \meta{keywords clist} 中的某一项，若匹配则移除它们。忽略大小写。
为了检测后面的文字是否匹配 \meta{keywords clist} 中的项，它会自动\emph{展开}后面的命令。

对 \meta{keyword clist} 中的每一项，先移除两端的空格和空项，
然后转为类别码为 12 的记号列（包括空格）。

它不会移除 \meta{keywords clist} 中重复的项，若有重复的项，则以最后出现的那个为准。

在 \meta{true} 和 \meta{false} 中可使用 \cs{l_collectn_keywords_int} 来获取匹配的
\meta{keyword} 的位置，\cs{l_collectn_keywords_str} 获取匹配的 \meta{keyword}。
\end{function}

\begin{function}{\collectn_set_keywords:Nn,
  \collectn_set_keywords:No,\collectn_set_keywords:NV,
  \collectn_set_keywords:Nv,\collectn_set_keywords:Ne,
  \collectn_set_keywords:cn,
  \collectn_set_keywords:co,\collectn_set_keywords:cV,
  \collectn_set_keywords:cv,\collectn_set_keywords:ce
}
  \begin{syntax}
    \V*|\collectn_set_keywords:NTF| \meta{tl} \marg{keyword clist}
  \end{syntax}
对 \meta{keyword clist} 中的每一项先移除两端的空格，再应用 \cs{collectn_set_keyword:Nn}，
然后将这些项保存至 \meta{tl}。

它不会移除 \meta{keywords clist} 中重复的项，若有重复的项，则以最后出现的那个为准。
\end{function}

\begin{function}{\collectn_set_keywords_from_seq:NN,
  \collectn_set_keywords_from_seq:Nc,\collectn_set_keywords_from_seq:cN,
  \collectn_set_keywords_from_seq:cc
}
  \begin{syntax}
    \V*|\collectn_set_keywords_from_seq:NN| \meta{tl} \meta{seq}
  \end{syntax}
对 \meta{seq} 中的每一项应用 \cs{collectn_set_keyword:Nn}，然后将这些项保存至 \meta{tl}。

它不会移除重复的项，若有重复的项，则以最后出现的那个为准。
\end{function}

\begin{function}[TF]{\collectn_scan_keywords:N,\collectn_scan_keywords:c,
  \collectn_scan_keywordscs:N,\collectn_scan_keywordscs:c}
  \begin{syntax}
    \V*|\collectn_scan_keywords:NTF| \meta{keywords tl} \marg{true} \marg{false}
  \end{syntax}
判断接下来的一些字符是否匹配 \meta{keywords tl} 中的某一项，若匹配则移除它们。
为了检测后面的文字是否匹配 \meta{keywords tl} 中的项，它会自动\emph{展开}后面的命令。

\meta{keywords tl} 需用 \cs{collectn_set_keywords:Nn} 或 
\cs{collectn_set_keywords_from_seq:NN} 设置。
\end{function}

\begin{function}{\l_collectn_keywords_int,\l_collectn_keywords_str}
\meta{keywords clist/tl} 中匹配的 \meta{keyword} 的位置和值。
若不匹配则位置为 0，值为空。
\end{function}


\section{\pkg{lt3ekeys}}\label{sec:lt3ekeys}

本宏包扩展了 \pkg{l3keys} 的部分功能。

\subsection{定义键}

\subsection{设置键}

\subsection{\pkg{lt3ekeys-elkernel}}

本宏包为 \LaTeX 和 \LaTeXiii 的一些有用的内部命令提供公共接口。

\begin{function}{\elkernel_arg_to_keyvalue:Nnn}
  \begin{syntax}
    \V*|\elkernel_arg_to_keyvalue:Nnn| \meta{result} \marg{key} \marg{arg}
  \end{syntax}
检查 \meta{arg} 是否为键值对，若不是，则把 \meta{key} 作为键，\meta{arg} 作为值。
将结果保存到 \meta{result} 中。

检查是否为键值对的方法和 \pkg{ltcmd} 的 \texttt = spec 一致。
\end{function}

\begin{function}[TF]{\elkernel_if_date_at_least:nn}
  \begin{syntax}
    \V*|\elkernel_if_date_at_least:nnTF| \marg{date_1} \marg{date_2} \marg{true} \marg{false}
  \end{syntax}
测试 \meta{date_1} 是否等于或晚于 \meta{date_2}。
年月日可用 \texttt{\textbackslash} 或 \texttt{-} 分隔，但不可混用。
\end{function}

\begin{xample}
\ExplSyntaxOn
\tl_to_str:N \fmtversion;
\elkernel_if_date_at_least:nnTF { \fmtversion } { 2024/06/01 } { t } { f }
\ExplSyntaxOff
\stopxamplecode
\xampleprint
\end{xample}

\subsection{定义命令——\pkg{lt3ekeyscmd}}\label{sec:lt3ekeyscmd}

本宏包实现了一个与 \cs{DeclareDocumentCommand} 类似的命令，提供了更多常用的功能。

以下称使用 \cs{DeclareDocumentCommand} 定义的命令为 \veta{document-cmd}，
称使用 \cs{ekeysdeclarecmd} 定义的命令为 \veta{ekeys-cmd}。

\begin{function}{\DeclareEKeysCommand,\ekeysdeclarecmd,
  \ekeys_declare_cmd:Nnn,\ekeys_declare_cmd_x:Nnn,
  \l_ekeys_cmd_defaults_bool}
  \begin{syntax}
    \V\ekeysdeclarecmd   <cmd> <{arg spec}> <{code}>
    \V\ekeysdeclarecmd * <cmd> <{arg spec}> <{code}>
    \V*|\ekeys_declare_cmd:Nnn|   <cmd> <{arg spec}> <{code}>
    \V*|\ekeys_declare_cmd_x:Nnn| <cmd> <{arg spec}> <{code}>
  \end{syntax}
类似于 \cs{DeclareDocumentCommand}，部分 \meta{arg spec} 未实现，增加了一些 \meta{arg spec}。

\cs{ekeysdeclarecmd} 等于 \cs{ekeys_declare_cmd:Nnn}。
\cs{ekeys_declare_cmd_x:Nnn} 完全展开 \meta{code}。

\cs{DeclareEKeysCommand} 是 \cs{ekeysdeclarecmd} 的另一个名字。

\meta{arg spec} 可以是数字，此时相当于 \cs{cs_generate_from_arg_count:NNnn} \meta{cmd}
\cs{cs_set_protected:Npn} \marg{arg spec} \marg{code}。

\meta{arg spec} 不为数字（或空）时定义的命令称为 \veta{ekeys-cmd}。

最大的参数数量为 9。

此命令定义的命令均为使用长参数。即它们定义的命令总是 \tn{protected}\tn{long}。
\end{function}

目前，在向后寻找可选参数时，会忽略空格，但未来可能会修改为不忽略空格，因此不宜在可选参数前
加上空格（但必需参数 \UseList[\texttt]{mrR}{、} 总忽略空格）。
如 \verb*|\foo {a} [o]| 应写为 \verb|\foo {a}[o]|，假定 \veta{arg spec} 为 \texttt{mo}。

在定义 \veta{ekeys-cmd} 时，若设置 \cs{l_ekeys_cmd_defaults_bool} 为真，
则可选参数的默认值可以引用其它实参（称为“执行可选参数默认值引用替换”），
否则 \meta{arg spec} 中的参数不能引用其它实参，
如 \verb|r[] D(){#1}| 中的 \verb|#1| 只是普通文本。
\cs{ekeysdeclarecmd} 不带星号的版本设置 \cs{l_ekeys_cmd_defaults_bool} 为假，
\cs{ekeysdeclarecmd} 带星号的版本设置 \cs{l_ekeys_cmd_defaults_bool} 为真。
\cs{l_ekeys_cmd_defaults_bool} 只有在使用上述三个命令时才会生效。
注意：若默认参数之间互相循环引用，则在执行时会出错，但 \veta{document-cmd} 则不会出错。
如命令的 spec 为 \verb|D(){#2} D<>{#1}|，则命令为 \veta{ekeys-cmd} 会出错，而为
\veta{document-cmd} 不会出错。

\veta{ekeys-cmd} 可以用于 \cs{ShowCommand}、\cs{NewCommandCopy} 中，也支持 \texttt{cmd} 钩子，限制条件和 \veta{document-cmd} 一致。

\begin{function}{\DeclareEKeysCollector,\ekeysdeclarecollector}
  \begin{syntax}
    \V\DeclareEKeysCollector   <collector> <{arg spec}>
    \V\DeclareEKeysCollector * <collector> <{arg spec}> <tl> <{do code}>
  \end{syntax}
定义和 \veta{ekeys-cmd} 类似的命令，\meta{arg spec} 的参数数量可以超过 9 个。
这种命令称之为 \veta{ekeys-collector}。
\end{function}

\begin{function}[rEXP]{\ekeyscollectorarg}
  \begin{syntax}
    \V\ekeyscollectorarg \marg{arg number}
  \end{syntax}
完全展开为第 \meta{arg number} 个参数。
如果没有该值，则为 \cs{q_no_value}，可以使用 \cs{IfQuarkNoValueTF} 检测。非正整数都无效。
它只能用于 \veta{ekeys-collector} 的 \meta{do code} 中。

注意：不能使用 \texttt f 展开，因为值可能为 \cs{q_no_value}，它会造成无限递归。
\end{function}

\begin{xample}
\DeclareEKeysCollector \faa { m m @w{ () [] } m m m m m m m }
\faa\tmp{\meaning\tmp.} 12 [[a]](b(b)) 56789ABCD.

\faa\tmp{\edef\tmp{\ekeyscollectorarg{10}}\meaning\tmp.} 
  12 [[a]](b(b)) 56789ABCD.

\DeclareEKeysCollector*\fbb { m m @w{ () [] } m m m m m m m } \tmp {\meaning\tmp.}
\fbb 12 [[a]](b(b)) 56789ABCD.
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\ekeysgenerategrabber}
  \begin{syntax}
    \V\ekeysgenerategrabber <grabber tl> <{arg spec}>
  \end{syntax}
从 \meta{arg spec} 生成 grabber。生成的 grabber 可以用于 \cs{ekeyscollectargs} 命令中。

预先生成 grabber 可以避免重复生成以节省时间。

默认最大可用的参数个数为 \tn{e@alloc@top}，它远远超过 9。在 \pdfLaTeX 和 \XeLaTeX 中为 
$2^{15}-1=32767$，在 \LuaLaTeX 和 (u)p\LaTeX 中为 $2^{16}-1=65535$。
可以通过 \cs{l_ekeys_cmd_max_args_int} 修改。
\end{function}

\begin{function}{\ekeys_grabber_args_do:NNn}
  \begin{syntax}
    \V*|\ekeys_grabber_args_do:NNn| \meta{tl} \meta{grabber tl} \marg{do code}
  \end{syntax}
首先收集 \meta{grabber tl} 所需的参数，每个参数都用 \verb|{}| 括起来，将其保存到 \meta{tl}
中，然后执行 \meta{do code}。

此命令不会执行可选参数默认值引用替换。
\end{function}

\begin{function}{\ekeys_collect_args_do:NNn,\ekeyscollectargs}
  \begin{syntax}
    \V*|\ekeys_collect_args_do:NNn| \meta{tl} <ekeys-cmd OR grabber tl> \marg{do code}
  \end{syntax}
首先收集 \meta{ekeys-cmd} 所需的参数或 \meta{grabber tl} 所需的参数，每个参数都用 
\verb|{}| 括起来，将其保存到 \meta{tl} 中，然后执行 \meta{do code}。

只有当第二个参数为 \veta{ekeys-cmd} 时，才会此执行可选参数默认值引用替换。
\end{function}

\begin{function}{\ekeys_collect_args_do:Nnn,\ekeyscollectargs*}
  \begin{syntax}
    \V*|\ekeys_collect_args_do:Nnn| \meta{tl} \marg{arg spec} \marg{do code}
  \end{syntax}
首先根据 \meta{arg spec} 收集所需的参数，每个参数都用 \verb|{}| 括起来，
将其保存到 \meta{tl} 中，然后执行 \meta{do code}。

最大可用的参数个数为 \tn{e@alloc@top}，它远远超过 9。在 \pdfLaTeX 和 \XeLaTeX 中为 
$2^{15}-1=32767$，在 \LuaLaTeX 和 (u)p\LaTeX 中为 $2^{16}-1=65535$。
可以通过 \cs{l_ekeys_cmd_max_args_int} 修改。

此命令相当于先用 \cs{ekeysgenerategrabber} 生成 grabber，然后再使用
\linebreak \cs{ekeyscollectargs}。

此命令不会执行可选参数默认值引用替换。
\end{function}

\begin{function}{l_ekeys_cmd_max_args_int}
最大的可收集的参数数目。
\end{function}

\begin{function}{\ekeys_cmd_undefine:N}
  \begin{syntax}
    \V*|\ekeys_cmd_undefine:N| \meta{ekeys-cmd}
  \end{syntax}
将 \meta{ekeys-cmd} （局部地）设置为未定义。如果不是 \veta{ekeys-cmd} 则\emph{保持不变}。
对 \veta{ekeys-cmd} 和 \veta{ekeys-collector} 都有效。
\end{function}

\begin{function}{\ekeysmakeglobal,\ekeys_cmd_global:N}
  \begin{syntax}
    \V\ekeysmakeglobal \meta{ekeys-cmd}
  \end{syntax}
把局部定义的 \meta{ekeys-cmd} 改为全局定义的。\cs{ekeysdeclarecmd} 总是局部地定义命令。
如果不是 \veta{ekeys-cmd} 则\emph{保持不变}。
\end{function}

\begin{function}[EXP]{\__ekeys_if_cmd:NTF}
  \begin{syntax}
    \V*|\__ekeys_if_cmd:NTF| \meta{cs} \marg{true code} \marg{false code}
  \end{syntax}
简单判断一个控制序列是否为 \veta{ekeys-cmd}。
\end{function}

\smallskip
{\def\CCD#1{\textcolor{black!60}{\texttt{#1}}}
\def\CCS#1{\textcolor{black}{\texttt{#1}}}
\def\CCE#1{\textcolor{red!90!black}{\texttt{#1}}}
可用的参数类型为：
\begin{itemize}
  \item[\CCS m] 标准的必须的参数；
  \item[\CCD r] \texttt r\meta{token_1}\meta{token_2}；
  \item[\CCD R] \texttt R\meta{token_1}\meta{token_2}\marg{default}；
  \item[\CCD o] 可选参数；
  \item[\CCD O] \texttt O\marg{default}；
  \item[\CCD d] \texttt d\meta{token_1}\meta{token_2}；
  \item[\CCD D] \texttt D\meta{token_1}\meta{token_2}\marg{default}；
  \item[\CCS s] 可选的星号，如果给出则为 \cs{BooleanTrue}，否则为 \cs{BooleanFalse}；
  \item[\CCS t] \texttt t\meta{token}，如果 \meta{token} 给出，则为 \cs{BooleanTrue}，否则为 \cs{BooleanFalse}；
  \item[\CCE k] \texttt k\marg{keyword}，如果 \meta{keyword} 给出，则为 \cs{BooleanTrue}，否则为 \cs{BooleanFalse}；定义命令时，忽略 \meta{keyword} 的类别码，执行时，仅比较字符，忽略类别码（活动字符除外，它将导致失配）和大小写的区别；另见 \cs{collectn_scan_keyword:nTF}；
  \item[\CCE t] \texttt t\marg{token pairs}，它占用两个参数，用于确定多个定界符对是否有一个给出了，第一个为定界符对所在的位置（如果未出现则为 $0$），第二个为它的值；
  \item[\CCE T] \texttt T\marg{token pairs}\marg{default}，它占用两个参数，用于确定多个定界符对是否有一个给出了，第一个为定界符对所在的位置（如果未出现则为 $0$），第二个为它的值（如果未出现则为 \meta{default}）；
  \item[\CCE p] \texttt p\marg{tokens}，如果出现在 \meta{tokens} 中，则给出所在的位置，否则给出 0；
  \item[\CCE P] \texttt P\marg{tokens}\marg{defaults}，\meta{defaults} 的长度必须为 \meta{tokens} 的长度加一，用出现在 \meta{tokens} 中的位置索引 \meta{defaults}（\meta{defaults} 的索引从 $0$ 开始），\verb|p{*-+}| 相当于 \verb|P{*-+}{0123}|；
  \item[\CCE e] \texttt e\marg{tokens}，使用 \texttt w 参数实现，每个符号可以重复使用；
  \item[\CCE E] \texttt E\marg{tokens}\marg{defaults}，使用 \texttt W 参数实现，每个符号可以重复使用；
  \item[\CCE w] \texttt w\marg{token pairs}，智能匹配多个 \texttt d 类型的参数，从而可以以任意顺序给出参数的值；
  \item[\CCE W] \texttt W\marg{token pairs}\marg{defaults}，智能匹配多个 \texttt D 类型的参数，从而可以以任意顺序给出参数的值；
  \item[\CCE g] 可选的以 \verb|{| 开始的参数，\verb|{| 可以是显式或隐式的；若后面跟着的不是 \verb|{| 而是 \verb|\relax|，则移除这个 \verb|\relax|；
  \item[\CCE G] \texttt G\marg{default}，可选的以 \verb|{| 开始的参数，\verb|{| 可以是显式或隐式的；若后面跟着的不是 \verb|{| 而是 \verb|\relax|，则移除这个 \verb|\relax|；
  \item[\CCS l] \verb|{| 前的内容，如没有 \verb|{| 则出错；
  \item[\CCS u] \texttt u\marg{tokens}，\meta{tokens} 前的内容，\meta{tokens} 会被移除，如没有 \meta{tokens} 则出错；
  \item[\CCE U] \texttt U\marg{tokens_1}\marg{tokens_2}，\meta{tokens_1} 前的内容，移除 \meta{tokens_1} 并留下 \meta{tokens_2}，如没有 \meta{tokens_1} 则出错；
  \item[\CCS v] 以 verbatim 的形式读取文字，该参数所含字符的类别码的可能值为 10、12、13（在 \pupTeX 引擎下，仅 Unicode 编码小于等于 255 的字符有此特性）；且 \texttt{\^{}\^{}M} 的类别码为 13；
\end{itemize}
}

未实现的参数类型为 \IterateList{b+!>=}{\texttt{#1}，} 使用它们仅给出警告，不会报错。

除非另有说明否则上述参数类型的参数中均不能出现显式或隐式的空格（\texttt\textvisiblespace）、宏变量字符（\texttt\#）、左右括号（\texttt{\{ \}}），即 \verb|d<#|、\verb|t\bgroup| 等均为错误用法。

\texttt u 和 \texttt U 的参数可以有空格，如可以 \verb|u{ }|，必须有括号。

带有左右定界符的参数在使用时不能嵌套，如 \meta{arg spec} 为 \verb|d<>|，则 \verb|\foo<a<b>>|
的参数为 \verb|a<b|，要得到 \verb|a<b>|，必须写 \verb|\foo<{a<b>}>|。

\pkg{lt3ekeysext} 宏包对其进行了进一步扩展，例如支持定义可嵌套的定界符以及更高的执行效率。见\cref{sec:lt3ekeysext}。

\begin{function}[EXP]{\ekeys_cmd_name:}
完全展开为 \veta{ekeys-cmd} 的名称。仅能用在 \veta{ekeys-cmd} 执行的 \meta{code} 中。
\end{function}

\begin{function}[EXP]{\IfQuarkNoValueTF,\IfQuarkNoValueT,\IfQuarkNoValueF}
  \begin{syntax}
    \V\IfQuarkNoValueTF \marg{arg} \marg{true code} \marg{false code}
  \end{syntax}
判断 \meta{arg} 是否为 \cs{q_no_value}。
\end{function}

\begin{function}[EXP]{\NumberCase,\@numbercase}
  \begin{syntax}
    \V\NumberCase \marg{integer} \{ \marg{case_0} \marg{case_1} ... \marg{case_n} \} \marg{else}
    \V\@numbercase <integer> ; \marg{case_0} \marg{case_1} ... \marg{case_n} ; \marg{else}
  \end{syntax}
根据 \meta{integer} 的值选择对应的 \meta{case_i}，如果 
$\veta{integer}>\veta{n}$ 或 $\veta{integer}<0$ 则使用 \meta{else}。

\cs{BooleanFalse} 相当于 0，\cs{BooleanTrue} 相当于 1。

展开两次即可得到结果。
\end{function}

\begin{xample}
\ExplSyntaxOn
\ekeys_declare_cmd:Nnn \foo { p{!@*-+} } { 位置为：#1. }
\ekeys_declare_cmd:Nnn \fee { p{!@*-+} } 
  { \NumberCase {#1} { {错误！} } { 给出 \tl_item:nn {!@*-+} {#1}. } }
\ekeys_declare_cmd:Nnn \fii { P{!@*-+}{ {错误}{!}{@}{*}{-}{+} } } { 给出：#1. }
\ExplSyntaxOff

\foo @ \par 
\foo ? \par 
\fee @ \par 
\fee ? \par
\fii @ \par 
\fii ?
\stopxamplecode
\xampleprint
\end{xample}

\begin{xample}
\ekeysdeclarecmd \foo { t{ () [] <> } } {位置为：#1，值为：#2。}
\foo <c> \foo (p) \foo\relax
\stopxamplecode 
\xampleprint
\end{xample}

\texttt w 和 \texttt W 中（包括 \texttt e 和 \texttt E）的定界符对可以重复使用，
但在第一个相同的情况下第二个也必须相同。
如支持 \verb|w{ [] () [] }|、\verb|w{ (] [] <] }|，但不支持 \verb|w{ [) [] }|。

\begin{xample}
\ekeysdeclarecmd \foo { w{ [] () <> } } {\{#1;#2;#3\}}
\ekeysdeclarecmd \fee { w{ [] [] () } } {\{#1;#2;#3\}}

\foo <a> (b) [c] , \foo (a) [b] <c> ,
\fee [a] (b) [c] , \fee [a] [b] (c) , \fee (a) [b] [c] ,
\stopxamplecode
\xampleprint 
\end{xample}

\UseList[\texttt]{tTwW}{、} 的每对的第二个还能为空或空格。
例如 \verb|w{ _{} ^{} }| 相当于 \verb|e{_^}|，例如支持 \verb|w{ [] () _{} ^{} :{ } }|。
但第一个不能为空或空格，如不支持 \verb|w{ {}_ }|。

\texttt t 和 \texttt T 用于仅需要一个参数的情况，这些定界符对只会检测一次，
而 \texttt w 和 \texttt W 则支持多个，可以以任意顺序给出。

\begin{function}[EXP]{\ekeys_exp_not_braced:n,
  \ekeys_exp_not_braced:o,\ekeys_exp_not_braced:V,\ekeys_exp_not_braced:v,
  \ekeys_exp_not_braced:f,\ekeys_exp_not_braced:e,
}
  \begin{syntax}
    \V*|\ekeys_exp_not_braced:n| \marg{text}
  \end{syntax}
展开为 \texttt\{\cs{exp_not:n}\marg{text}\texttt\}。
\end{function}

\begin{function}{\ekeys_cmd_after_declare:n,\ekeys_cmd_after_declare:e}
  \begin{syntax}
    \V*|\ekeys_cmd_after_declare:n| \marg{code}
  \end{syntax}
定义完本命令后执行 \meta{code}。在定义命令的外面使用是无效的。
\end{function}

\subsection{定义命令扩展——\pkg{lt3ekeysext}}\label{sec:lt3ekeysext}

\pkg{lt3ekeysext} 进一步扩展了 \pkg{lt3ekeyscmd} 的功能。

为 \cs{ekeysdeclarecmd} 增加了几个前缀：
\begin{itemize}
  \item[\texttt\#] 将其后的那个参数类型标记为需要特殊处理（如果可以的话），使用此前缀能（大大）提高 \veta{ekeys-cmd} 的执行速度，但有轻微的副作用（一般很难发生，详见后文）；支持
    \UseList[\texttt]{TpPuUeEwW}{、}；
  \item[\texttt @] 将其后的那个参数类型标记为需要嵌套（如果可以的话）；
  \item[\texttt\&] 将其后的那个参数类型标记为保留等价的原始输入；支持 
    \UseList[\texttt]{rRoOdD}{、}，\UseList[\texttt]{stp}{、}，
    \texttt k，\texttt t、\texttt T，\texttt g、\texttt G，
    \UseList[\texttt]{eEwE}{、}；
\end{itemize}

在使用 \texttt\# 前缀时，参数在其定界符为控制序列的时候，定义和使用 \veta{ekeys-cmd} 时 
\tn{escapechar} 的值必须为相同的正数。如，当定义 \cs[no-index]{foo} 时 \tn{escapechar}
为 \texttt\textbackslash，则使用 \cs[no-index]{foo} 时也必须为 \texttt\textbackslash。
受影响的参数类型为 \UseList[\texttt]{tTpPeEwW}{、}。

使用 \UseList[\texttt]{tTeEwW}{、} 时，设置 \texttt\& 将自动设置 \texttt\#。

对于 \texttt s、\texttt t 和 \texttt k 参数，使用 \texttt\& 前缀，
在检测到存在此 \meta{token}（或 \meta{keyword}）时，
会把这个参数设置为该 \meta{token}（或 \meta{keyword}），否则这个参数为空。
使用其它几个支持 \texttt\& 前缀的参数时，若检测到有此定界符，则该参数会包含此定界符，
否则该参数为 \meta{default}。

\begin{xample}
\newcommand{\abdel}[5]{#1#3#4#2#5}
\ekeysdeclarecmd\ab{ p{\big\Big\bigg\Bigg*} &t{ \{\} [] () || } }
  {\IfNoValueTF{#3}{NAN}% 如果没有所列的括号，输出 NAN
    {\NumberCase{#1} {
        {\abdel\left\right}
        {\abdel\bigl\bigr} {\abdel\Bigl\Bigr}
        {\abdel\biggl\biggr} {\abdel\Biggl\Biggr} {\abdel{}{}}
      }{}
     #3}
  }

$ \ab[\dfrac12] $, $ \ab\Big(\dfrac12) $, $ \ab*|\dfrac12| $, $ \ab? $
\stopxamplecode
\xampleprint
\end{xample}

\begin{xample}
\ekeysdeclarecmd\foo{ &s &t{ () [] } &w{ !{} +{ } } &k{p_t} }
  { 1.\{#1\}, 2.\{#2|#3\}, 4.\{#4\}\{#5\} 6.\{#6\} }
\ttfamily
{\catcode`\_=13 \foo *[b]+plus !{mu} p_t} , % 此时 _ 为活动字符
{\catcode`\_=8  \foo *[b]+plus !{mu} p_t} , % 此时 _ 为下标
\foo (b)!mu ,
\stopxamplecode
\xampleprint
\end{xample}

执行效率从慢到块大致如下：
无 \texttt\# 且为 \UseList[\texttt]{tTwW}{、} 的 \veta{ekeys-cmd} $<$
无 \texttt\# 且有 \texttt @ 的 \veta{ekeys-cmd} $<$
\veta{document command} $<$
带 \texttt\# 和 \texttt @ 的 \veta{ekeys-cmd} $<$ 
带 \texttt\# 的 \veta{ekeys-cmd} $<$
\cs{ekeys_grabber_args_do:NNn} $<$ \cs{__ekeys_grabber_args_do:Nnn}。

为 \cs{ekeysdeclarecmd} 增加了几个参数类型：
\begin{itemize}
  \item[\texttt c] \texttt c\marg{collect spec}。特殊的保存方式；必须的参数；
  \item[\texttt C] \texttt C\marg{allocated collect spec}。特殊的保存方式；必须的参数；
  \item[\texttt K] \texttt K\marg{scanner-and-args}。自定义的参数扫描器。
\end{itemize}

\texttt{c} 和 \texttt{C} 用 \TeX 寄存器来保存各种值，设置这些值的方式和 \TeX 相似。
前者会自动分配一个新的寄存器，而 \texttt{C} 使用已经分配好的寄存器，这是它们的唯一区别。
这两个参数类型的实参为所分配的寄存器，一般情况下不应直接修改这个寄存器。

如
\begin{xample}
\ekeysdeclarecmd\foo{ c{i} m }{[\the#1,\detokenize{#2}]}
\ttfamily
\foo -12 {a}; \quad \foo -12 \relax; \quad \foo -12\relax;
\foo \inteval{12+8*(-2)}\relax; \foo \numexpr 12+8*(-2)\relax;
\stopxamplecode
\xampleprint
\end{xample}
定义了一个命令 \verb|\foo|，第一个参数存储一个整数，第二个参数为通常的参数。注意到
给出第一个参数时与通常的使用方式很不相同，这是 \TeX 中为寄存器赋值的语法，每个类型都
遵循各自的语法规则，详细用法见 \textit{The \TeX book}。

\startfullpagewidth<\bodylmargin>
\meta{collect spec} 可用的值如下：
\begin{itemize}
  \item[\texttt i] 为 \texttt{int}（\texttt{count}）寄存器赋值；
  \item[\texttt d] 为 \texttt{dim}（\texttt{dimen}）寄存器赋值；
  \item[\texttt s] 为 \texttt{skip} 寄存器赋值；
  \item[\texttt m] 为 \texttt{muskip} 寄存器赋值；
  \item[\texttt t] 为 \texttt{toks} 寄存器赋值；
  \item[\texttt b]\meta{extra spec}，为一个水平盒子赋值；\marg{extra spec} 可以为“\texttt{*}”表示可自由设置盒子宽度；也可为 \oarg{width}\oarg{pos}，正如 \tn{makebox} 的前两个参数；
  \item[\texttt w]\meta{extra spec}，为一个固定宽度的盒子赋值；类似于把参数放在一个\env{minipage} 里；\meta{extra spec} 可以为 \oarg{vpos}\oarg{height}\oarg{inner pos}\marg{width}，正如 \tn{parbox} 的前四个参数一样。若只给出 \meta{width} 则为垂直盒子，且可以对其使用 \tn{vsplit}，否则为水平盒子；
  \item[\texttt v]\meta{extra spec}，为一个有最大宽度的盒子赋值；类似于把参数放在一个 \env{varwidth} 里；\meta{extra spec} 可以为 \oarg{vpos}\oarg{height}\oarg{inner pos}\marg{width}，正如 \tn{parbox} 的前四个参数一样。若只给出 \meta{width} 则为垂直盒子，且可以对其使用 \tn{vsplit}，否则为水平盒子。
\end{itemize}
注意 \meta{extra spec} 的外侧不能有花括号，除非是只有 \meta{width} 这个必须参数。
并且可选参数之间不能有额外的空格。
如 \verb|b{*}|、\verb|b[3cm]|、\verb|w{3cm}|、\verb|w[c]{3cm}| 可以，
\verb|b{[3cm]}|、\verb|b{[3cm][l]}|、\verb|w{[c]{3cm}}| 不可以。

前 5 个类型，即 \UseList[\texttt]{idsmt}{、}，的实参
可以作为 \LaTeXiii 函数的 \texttt{V} 变体的参数。

\meta{allocated collect spec} 是一个已经分配的寄存器加上 \meta{collect spec}，若 
\meta{collect spec} 的类型没有必须参数，则可以省略不写，只写寄存器，但寄存器必须和类型一致。

\meta{collect spec} 和 \meta{allocated collect spec} 中不能引用其它实参，因为它们并非默认值。
\stopfullpagewidth

\begin{xample}
\newdimen\tmpadim 
\ekeysdeclarecmd\foo { C{\tmpadim} c{i} c{b[2cm][c]} } 
  { [\the#1, \the#2, \fbox{\box#3}] }
\foo 12pt -1 {a a}
\foo \dimexpr 5cm+12pt\relax \numexpr 3+4*(1-2)\relax {a a}
\stopxamplecode
\xampleprint
\end{xample}

\begin{xample}
\ekeysdeclarecmd \myfbox { c{b} } {\fbox{\box#1}}
\myfbox{\verb|\myfbox| 与 \verb|\fbox|}
\myfbox{\parbox{3cm}{可分段的\par fbox}}
\stopxamplecode
\label{eg:myfbox-cmd}
\xampleprint
\end{xample}

\begin{function}{\ekeys_cmd_new_scanner:nnnpn,\ekeys_cmd_new_scanner:nnpn,
  \ekeys_cmd_new_scanner:nnnpx,\ekeys_cmd_new_scanner:nnpx}
  \begin{syntax}
    \V*|\ekeys_cmd_new_scanner:nnnpn| \marg{scanner} \marg{argument numbers}
    ~~~~\marg{initial action} \meta{parameter list} \marg{scanner action}
    \V*|\ekeys_cmd_new_scanner:nnpn| \marg{scanner} \marg{argument numbers}
    ~~~~\meta{parameter list} \marg{scanner action}
  \end{syntax}
为参数类型 \texttt{K} （局部地）定义新的扫描器 \meta{scanner}。
该扫描器为 \veta{ekeys-cmd} 增加 \meta{argument numbers} 个参数。

\meta{initial action} 为定义 \veta{ekeys-cmd} 时要执行的操作。
可以使用参数，分别为 \veta{ekeys-cmd} 的名称，给该扫描器的额外的参数，
此 \veta{ekeys-cmd} 已有的参数个数，是否为 raw 参数（使用了 \verb|&|），
以及是否为 nested 参数（使用了 \verb|@|）。

注意，直接在 \meta{initial action} 中定义 \veta{ekeys-cmd} 和 \veta{ekeys-collector}
是不行的。但可以在其中使用 \cs{ekeys_cmd_after_declare:n}，先定义完本命令后再定义新的命令。
\end{function}

给扫描器的额外的参数指的是：
移除 \meta{scanner-and-args} 的首尾空格后，若它以一对 \verb|{ }| 开始，则这个花括号里的内容
就是 scanner，之后的内容移除掉两端的空格后就是额外的参数；
否则，就是 scanner 就是第一个花括号之前的内容（移除掉两端的空格），
额外的参数就是第一个花括号及其之后的记号。
如若 \meta{scanner-and-args} 为 \verb*| {a b } [v] j |，则 scanner 为 \verb*|a b |，
参数为 \verb*|[v] j|；
若 \meta{scanner-and-args} 为 \verb*| fo o {da} [b ] |，则 scanner 为 
\verb*|fo o|，参数为 \verb*|{da} [b ]|。

\meta{scanner action} 为 \veta{ekeys-cmd} 执行到该扫描器时需要执行的操作。
\meta{parameter list} 为需要的形参列表。
\meta{scanner action} 中，使用 \cs{ekeys_cmd_add_args:n} 为 \veta{ekeys-cmd} 添加实参。
\meta{scanner action} 完成时，\emph{必须}执行 \cs{ekeys_cmd_scanner_end:}。

注意 \cs{ekeys_cmd_name:} 不能在 \meta{initial action} 中使用（可以用 \verb|#1| 替代），
但可以在 \meta{scanner action} 中使用。另见 \cs{ekeys_cmd_args:}。

\cs{ekeys_cmd_name:} 与 \meta{initial action} 的第一个可用的参数相同，
\cs{ekeys_cmd_args:} 与 \meta{scanner action} 的第三个可用参数相同。

\begin{function}{\ekeysnewscanneralias,
  \ekeys_cmd_new_scanner_alias:nn,\ekeys_cmd_new_scanner_alias:nnn}
  \begin{syntax}
    \V\ekeysnewscanneralias \marg{alias} \marg{scanner definition}
    \V\ekeysnewscanneralias \marg{alias} \oarg{spec name} \marg{part spec}
    \V*|\ekeys_new_scanner_alias:nn|  \marg{alias} \marg{scanner definition}
    \V*|\ekeys_new_scanner_alias:nnn| \marg{alias} \marg{spec name} \marg{part spec}
  \end{syntax}
（局部地）定义扫描器的别名。

\meta{scanner definition} 是用在 \texttt K 的 \meta{scanner-and-args} 里的内容。
\meta{spec name} 是标识参数类型的字母，\meta{part spec} 是此类型具体的一个。
见\cref{eg:K-alias}。

当使用 \verb|K{|u\meta{args}\verb|}| 时，只使用 \meta{args} 的第一项，
多余的部分会放到 \meta{scanner definition} 或 \meta{part spec} 后面。
\end{function}

有几个预定义的扫描器：
\startfullpagewidth<\bodylmargin>
\begin{description}
  \item \texttt{norelax}，移除一个 \tn{relax}，在向后寻找这个 \tn{relax} 时\emph{忽略}空格；
  \item \texttt{norelax!}，移除一个 \tn{relax}，在向后寻找这个 \tn{relax} 时\emph{不忽略}空格；
  \item \texttt ?\meta{preprocessor spec}，参数预处理器。\meta{preprocessor spec} 用法为：
  \begin{itemize}
    \item \marg{ekeys-cmd arg spec}，使用 \meta{ekeys-cmd arg spec} 获取后面的参数，转化为带花括号的标准参数；
    \item \marg{ekeys-cmd arg spec}\marg{scanner code}，使用 \meta{ekeys-cmd arg spec} 获取后面的参数，并用 \meta{scanner code} 替换这些参数。\meta{scanner code} 必须包含 \cs{ekeys_cmd_scanner_end:}，且在它后面的内容会被 \veta{ekeys-cmd} 重新读取；
    \item \marg{ekeys-cmd arg spec}\marg{scanner code}\meta{text}，同上，\meta{text} 会放在要读取的参数之前。
  \end{itemize}
  \item[\texttt u] \texttt u\marg{alias}，使用由 \cs{ekeysnewscanneralias} 设置的别名。
  \item[\texttt{define}] 可以用它来在定义 \veta{ekeys-cmd} 时，进一步的自定义参数获取方式。
  用法为 \verb|K{define|\meta{define spec}\verb|}|。\meta{define spec} 可以为：
  \begin{itemize}
    \item \marg{definition}，它不占用任何参数。主要用作向后检查，或展开，也可以向输入中添加额外的内容。需要在 \meta{definition} 的合适位置添加 \cs{ekeys_cmd_add_args:n} 和 \cs{ekeys_cmd_scanner_end:}；
    \item \marg{numbers}\marg{parameters}，它占用 \meta{numbers} 个参数。根据 \meta{parameters} 向后获取参数。\meta{parameters} 的参数数量不能少于 \meta{numbers}。这是 \tn{def} 的 \veta{ekeys-cmd} 接口；
    \item \marg{numbers}\marg{parameters}\marg{definition}，同上。注意：当 $\veta{numbers}\geq 0$ 时，会在 \meta{definition} 后面自动加上 \cs{ekeys_cmd_add_args:n} 和 \cs{ekeys_cmd_scanner_end:}；
    \item \texttt{\{*\meta{numbers}\}}\marg{parameters}\marg{definition}，\meta{numbers} 与 \meta{parameters} 没有关系。但需要在 \meta{definition} 的合适位置添加 \cs{ekeys_cmd_add_args:n} 和 \cs{ekeys_cmd_scanner_end:}，且添加的参数必须和 \meta{numbers} 一致。
  \end{itemize}
  \item[\texttt{lohi}] 它获取一组数学上下标，占用 2 个参数，第一个为下标，第二个为上标。
  如果不存在，则为使用默认值。设置默认值是通过给扫描器的额外的参数来设置：
  \verb|K{lohi|\meta{defaults}\verb|}|，其中 \meta{defaults} 为：
  \begin{itemize}
    \item \marg{default_{lo}}\marg{default_{hi}}，设置下标和上标的默认值；
    \item \marg{default}，设置上下标的默认值；
    \item 空，表示上下标使用特殊的标记：\UseName{c_novalue_tl}。
  \end{itemize}
  它支持 raw 修饰符，即可以自动添加 \verb|_| 和 \verb|^|，但默认值不会自动添加这两个符号。
\end{description}
\stopfullpagewidth

\begin{xample}
\ekeysdeclarecmd\faa{ K{lohi} }{[#1.#2]}
\ekeysdeclarecmd\fbb{ K{lohi{}} }{[#1.#2]}
\ekeysdeclarecmd\fcc{ &K{ lohi {_x}{^y} } } {[$\int #1#2$]}

\faa ^b_a; \faa \relax _a ^\relax b; \faa ^b; \faa ?; \par 
\fbb ^b; \fbb _a^b; \fbb ?; \par 
\fcc _a^b; \fcc _a; \fcc ?;
\stopxamplecode
{\catcode`\_=8 \xampleprint}
\end{xample}

\begin{xample}
\ekeysnewscanneralias{math-style}[p]
  {p{\displaystyle\textstyle\scriptstyle\scriptscriptstyle}}
\ekeysnewscanneralias{scan-bra-ket}{define{2}{<#1|#2>}}

\DeclareEKeysCommand \foo { &K{u{math-style}} K{u{scan-bra-ket}} }
  {{#1\left<#2\middle|#3\right>}}
$ \foo<a|b> $\quad $ \foo\displaystyle<\sum|\prod> $.
\stopxamplecode
\label{eg:K-alias}\xampleprint
\end{xample}

除了 \texttt ? 参数扫描器外，还有一个预处理指示符 \texttt ?，用法为 
\texttt ?\marg{preprocessor action}。预处理器指示符可以看成是 \texttt ? 参数扫描器
的预先构建的版本。

\begin{function}{\l_ekeys_cmd_scanner_args_int}
实际上，scanner 的参数数量不仅可以通过 \meta{argument numbers} 设置，还可以
在 \meta{initial action} 中直接修改 \cs{l_ekeys_cmd_scanner_args_int} 来设置参数数量。
\end{function}

\begin{function}{\ekeys_cmd_scanner_end:,\EKeysEndPreprocessor}
参数扫描器完成时，必须执行该命令。放在此命令之后的内容会添加到输入中，可供后面的参数解析。

如果不是用在扫描器或预处理器内，\cs{ekeys_cmd_scanner_end:} 会出错，
而 \cs{EKeysEndPreprocessor} 则什么也不做。预处理器和自定义的参数扫描器必须包含它们之一。
\end{function}

\begin{xample}
\ExplSyntaxOn
\ekeys_cmd_new_scanner_alias:nn { replace-keyword-to-num }
  {
    define 
      {
        \collectn_scan_keywords:nTF { true, false, on, off, yes, no }
          {
            \int_if_odd:nTF \l_collectn_keywords_int
              { \ekeys_cmd_scanner_end: 1~ }
              { \ekeys_cmd_scanner_end: 0~ }
          }
          { \ekeys_cmd_scanner_end: -1~ }
      }
  }
\DeclareEKeysCommand \foo { K{u{replace-keyword-to-num}} u{~} } {[#1]}
\ExplSyntaxOff
\foo TRUE; \foo on; \foo false; \foo ;
\stopxamplecode
\xampleprint
\end{xample}

\begin{function}{\ekeys_cmd_add_arg:n,\ekeys_cmd_add_arg:o,
  \ekeys_cmd_add_arg:V,\ekeys_cmd_add_arg:v,
  \ekeys_cmd_add_arg:f,\ekeys_cmd_add_arg:e}
  \begin{syntax}
    \V*|\ekeys_cmd_add_arg:n| \marg{argument}
  \end{syntax}
为 \veta{ekeys-cmd} 添加 $1$ 个实参。
\meta{scanner action} 中添加的实参总数量必须和定义扫描器时设置的数目一致。
\end{function}

\begin{function}{\ekeys_cmd_add_args:n,\ekeys_cmd_add_args:o,
  \ekeys_cmd_add_args:V,\ekeys_cmd_add_args:v,
  \ekeys_cmd_add_args:f,\ekeys_cmd_add_args:e}
  \begin{syntax}
    \V*|\ekeys_cmd_add_args:n| \{ \marg{argument_1} ... \marg{argument_n} \}
  \end{syntax}
为 \veta{ekeys-cmd} 添加 $n$ 个实参。
\meta{scanner action} 中添加的实参总数量必须和定义扫描器时设置的数目一致。
\end{function}

\begin{function}{\ekeys_cmd_register_cs:N,\ekeys_cmd_register_cs:c}
  \begin{syntax}
    \V*|\ekeys_cmd_register_cs:N| \meta{auxiliary function}
  \end{syntax}
仅当 \meta{auxiliary function} 不能在不同命令之间共享时才需注册，否则可以直接在外层定义，
不必置于 \meta{initial action} 之内。

注册命令的主要作用是使用 \cs{ekeys_cmd_undefine:N} 时，把这些注册的命令也设置为未定义。
\end{function}

\begin{function}{\ekeys_cmd_scanner_undefine:n,\ekeys_cmd_scanner_alias_undefine:n}
  \begin{syntax}
    \V*|\ekeys_cmd_scanner_undefine:n| \marg{scanner}
    \V*|\ekeys_cmd_scanner_alias_undefine:n| \marg{scanner alias}
  \end{syntax}
将 \meta{scanner} 或 \meta{scanner alias} \emph{局部地}设置为未定义。
\end{function}

\begin{function}[EXP]{\ekeys_cmd_args:}
完全展开为到目前为止此 \veta{ekeys-cmd} 已经使用了的参数数目。
仅能用于 \meta{scanner action}。可与 \cs{ekeys_cmd_name:} 配合使用。
\end{function}

请看下面两个例子了解它们的用法。

\begin{xample}
\ExplSyntaxOn
\ekeys_cmd_new_scanner:nnnpn { my/if-pt } { 1 }
  {
    \ekeys_cmd_register_cs:c { #1-#3-my/if-pt/is-raw }
    \bool_new:c { #1-#3-my/if-pt/is-raw }
    \bool_set:cn { #1-#3-my/if-pt/is-raw } {#4}
  }
  {
    \bool_set_eq:Nc \l_tmpa_bool 
      { \ekeys_cmd_name: - \ekeys_cmd_args: - my/if-pt/is-raw }
    \collectn_scan_keyword:nTF { pt }
      {
        \bool_if:NTF \l_tmpa_bool
          { \ekeys_cmd_add_args:e { { \tl_to_str:n { pt } } } }
          { \ekeys_cmd_add_args:n { { \BooleanTrue } } }
        \ekeys_cmd_scanner_end:
      }
      {
        \bool_if:NTF \l_tmpa_bool
          { \ekeys_cmd_add_args:n { {} } }
          { \ekeys_cmd_add_args:n { { \BooleanFalse } } }
        \ekeys_cmd_scanner_end:
      }
  }
\ExplSyntaxOff
\ttfamily
\ekeysdeclarecmd \foo { &K{my/if-pt} m } {[#1](#2)}
\DeclareCommandCopy{\foocopied}{\foo} 
\AddToHookWithArguments{cmd/foocopied/before}{\{#1|#2\}}
\foocopied pt; \foocopied PT; \foocopied p @; \quad 
\foo pt; \foo PT; \foo p @;
\stopxamplecode
\xampleprint
\end{xample}

\begin{xample}
\ExplSyntaxOn
\dim_new:N \l__my_scan_whd_dim % 临时保存长度
\seq_new:N \l__my_scan_whd_seq % 保存三个值，width,height,depth
\cs_generate_variant:Nn \seq_set_item:Nnn { NnV }
\collectn_set_keywords:Nn \c__my_scan_whd_tl { width, height, depth }
% 3 个参数，为 width, height, depth
\ekeys_cmd_new_scanner:nnpn { my/scan-whd } { 3 }
  {
    % initial seq
    \seq_clear:N \l__my_seq_whd_seq
    \seq_put_right:NV \l__my_seq_whd_seq \c_novalue_tl
    \seq_put_right:NV \l__my_seq_whd_seq \c_novalue_tl
    \seq_put_right:NV \l__my_seq_whd_seq \c_novalue_tl
    \__my_scan_whd_next: 
  }
\cs_new:Npn \__my_scan_whd_next: 
  {
    \collectn_scan_keywords:NTF \c__my_scan_whd_tl 
      {
        \collectn_value:Nnw \l__my_scan_whd_dim 
          {
            \seq_set_item:NnV \l__my_seq_whd_seq 
              { \l_collectn_keywords_int } \l__my_scan_whd_dim 
            \__my_scan_whd_next: 
          } =
      }
      { 
        \ekeys_cmd_add_args:e
          { \seq_map_function:NN \l__my_seq_whd_seq \ekeys_exp_not_braced:n }
        \ekeys_cmd_scanner_end: 
      }
  }
\ExplSyntaxOff

\ekeysdeclarecmd \foo { K{my/scan-whd} } 
  {[ W: \IfValueTF{#1}{#1}{--}; H: \IfValueTF{#2}{#2}{--}; D: #3 ]}
\foo height 3pt depth \dimeval{3pt+2pt-10pt} height \dimexpr 3pt-2pt wide
\stopxamplecode
\xampleprint
\end{xample}


\setuplayout{preset=balance}
\setuptitle[chapter,section]{numbering=false}

\chapter{TODO}

\newcommand\DN{\faCheckSquare[regular]} % done
\newcommand\HDN{\faMinusSquare[regular]} % half done
\newcommand\NDN{\faSquare[regular]} % has not done
\newcommand\SDN{\faPlusSquare[regular]} % need to be specified

\goodbreak
\begin{itemize}[midpenalty=-1000,
  label=\protect\raisebox{-.3ex}{\NDN}]
  \item 实现宏包加载机制；
  \item 完善 \pkg{buffer} 及其文档；
  \item 更好的文档；
  \item 实现 \cusmodule{pgf} 库；
  \item 适配 \pkg{tcolorbox}；
  \item 提供 \pkg{cleveref} 宏包的接口；
  \item \cls{cusclass} 文档类；
  \item 更好的竖排文档支持；
  \item \cusmodule{struct} 模块支持 \pkg{titlesec} 的所有标题形状；
  \item 更多丰富的标题设置；
  \item \cusmodule{struct} 模块支持 \hologo{KOMAScript} 的目录设置方式；
  \item 优化现有的局部目录实现方式；
  \item \cusmodule{struct} 模块简单支持 \pkg{titletoc} 的目录设置方式；
  \item 适配 latex-lab；
  \item 文档部件（frontmatter，mainmatter，appendix 等）；
  \item 注记支持（脚注、边注、尾注等）；
  \item \cuslibrary{colorful} 库，彩色；
  \item \cuslibrary{textdeco} 库，文字装饰；
  \item 完善 \cuslibrary{doc} 库；
  \item 子文档；
  \item 更加适配参考文献；
  \item 更加适配术语表；
  \item 实现 \cuslibrary{hooks} 库；
  \item 实现 \cuslibrary{external} 库；
  \item tagged pdf；
  \item 实现 \pkg{splitidx}；
  \item 更多测试；
  \item \TeX、\LaTeX 内部命令的接口？
  \item \pkg{multicolrule}？
  \item ……
\end{itemize}



\cleardoublepage 
\cussetup[doc/cmd]{hyper=false}
\vbadness=10000 \hbadness=10000 \vfuzz=\maxdimen \hfuzz=\maxdimen
\backmatter
\long\def\sectionmark#1{}
\chapter{索引}
\vskip-14pt 
% \printindex 
% \PrintChanges 
\PrintUsages 


%% list of hackings
\begingroup 
\setuponetitle{section}{beforeskip=15pt,afterskip=12pt,fixskip}

\ExplSyntaxOn
\cs_new:Npn \__this_if_liii:nTF #1
  { \etl_if_in:nnTF { \s_stop #1 } { \s_stop l3 } }
\cs_new:Npn \__this_if_le:n #1
  {
    \str_if_eq:eeTF { latex } { \str_lowercase:n {#1} }
      { \LaTeXe 内核 }
      {
        \str_if_eq:eeTF { latex3 } { \str_lowercase:n {#1} }
          { \LaTeXiii 内核 }
          {
            \etl_if_in:nnTF { \s_stop #1 } { \s_stop lt3 }
              { \textsf {#1} }
              {
                \etl_if_in:nnTF { \s_stop #1 } { \s_stop lt }
                  { \LaTeXe 内核 } { \textsf {#1} }
              }
          }
      }
  }
\cs_new:Npn \__this_detect_file:n #1
  {
    \cus_act_case_true:nnnn {#1}
      {
        { \str_if_eq:nnTF { latex } } { \LaTeXe 内核 \use_none:n }
        { \__this_if_liii:nTF }       { \LaTeXiii 的 \textsf }
      }
      { \__this_if_le:n }
      { {#1} }
  }
\cs_new:Npn \__this_detect_cus_other:n #1 { \texttt { cus.#1.tex } }
\cs_new:Npn \__this_detect_cus:n #1 
  { 
    \cus_act_case_true:nnnn {#1}
      {
        { \str_if_eq:nnTF { cus      } } { \texttt{cus.sty} \use_none:n }
        { \str_if_eq:nnTF { cusclass } } { \texttt{cusclass.cls} \use_none:n }
      }
      { \__this_detect_cus_other:n }
      { {#1} }
  }
\cs_new_eq:NN \CHDetectCUS \__this_detect_cus:n 
\cs_new_eq:NN \CHDetectFile \__this_detect_file:n
\ExplSyntaxOff
\newcommand\CHFrom[1]{\CHDetectCUS{#1}}
\newcommand\CHToI[2]{使用了 \CHDetectFile{#1} 的内部命令（或环境）：#2}
\newcommand\CHToP[2]{patch 了 \CHDetectFile{#1} 的命令（或环境）：#2}
\newcommand\CHToH[2]{hack 了 \CHDetectFile{#1} 的命令（或环境）：#2}
\newcommand\CHToM[2]{修改了 \CHDetectFile{#1} 的命令（或环境）：#2}
\newcommand\CHToA[2]{为 \CHDetectFile{#1} 增加了：#2}


\chapter{List of Hackings}

本章列出 \CusTeX 宏集中使用的内核内部命令、其它宏包的内部命令以及所有 hacking。

\linespread{1}\selectfont \small 
\obeylines

\CHToA{l3text}{\cs{text_mdfive_hash:n}};
\CHToA{l3token}{\cs{token_if_cs_word:NTF}, \cs{token_if_control_word:NTF}};

\section{\CHFrom{module.ltx}}

\CHToI{latex}{\tn{@dischyph}};
\CHToI{latex}{\tn{input@path}};
\CHToI{graphicx}{\tn{Ginput@path}, \tn{Ginclude@graphics}};
\CHToA{graphicx}{\tn{IfGraphicsExists}, \tn{InputIfGraphicsExists}};

\section{\CHFrom{module.util}}
\CHToI{l3seq}{\cs{__seq_push_item_def:n}, \cs{__seq_pop_item_def:}, \cs{s__seq}, \cs{__seq_item:n}};
\CHToI{latex}{\tn{c@page}};
\CHToI{latex}{\tn{@onlypreamble}, \tn{@notprerr}};
\CHToI{latex}{\tn{@twoclasseserror}};
\CHToA{l3keys}{\cmd[module=keys]{.switch_set:N}, %
  \cmd[module=keys]{.clist_put_right:N}, \cmd[module=keys]{.clist_put_left:N}, %
  \cmd[module=keys]{.toks_put_right:N}, \cmd[module=keys]{.toks_put_left:N}, %
  \cmd[module=keys]{.obey_psrrule:nn}, \cmd[module=keys]{.gbey_psrrule:nn}};
\CHToI{hyperref}{\tn{@filecolor}, \tn{@linkcolor}, \tn{@citecolor}, \tn{@urlcolor}};
\CHToI{latex}{\tn{@auxout}, \tn{@currentlabel}};
\CHToP{bookmark}{\tn{BKM@hook}};
\CHToM{bookmark}{\tn{bookmark@text}};
\CHToI{hyperref}{\tn{@currentHref}, \tn{ifHy@pdfstring}};
\CHToI{hyperref}{\tn{hyper@anchor}, \tn{hyper@anchorstart}, \tn{hyper@anchorend}, %
  \tn{hyper@link}, \tn{hyper@linkstart}, \tn{hyper@linkend}, %
  \tn{hyper@linkfile}, \tn{hyper@linkurl}};
\CHToI{pdfmanagement-testphase}{\cs{hyper@linklaunch}, \cs{hyper@linknamed}};
\CHToI{hyperref}{\cs{l__hyp_target_create_bool}, \cs{__hyp_target_manual:nn}, %
  \cs{__hyp_target_manual:nn}, \cs{__hyp_target_counter_anon:n}};
\CHToI{hyperref}{\hook{__hyp/target/setname}};
\CHToP{hyperref}{\tn{HyPL@EveryPage}};
\CHToM{hyperref}{\tn{HyPL@page}, \tn{HyPL@thisLabel}};
\CHToI{hyperref}{\tn{Hy@SaveSpaceFactor}, \tn{Hy@RestoreSpaceFactor}};


\section{\CHFrom{module.algo}}
\CHToI{l3tl}{\cs{__tl_map_function:Nnnnnnnnn}, \cs{\__tl_map_tokens:nnnnnnnnn} \cs{s__tl_stop}};


\section{\CHFrom{module.layout}}
\CHToI{geometry}{\tn{Gm@warning}};
\CHToI{geometry}{\tn{Gm@save}};
\CHToI{geometry}{\tn{Gm@restore}};
\CHToA{geometry}{\tn[no-index]{Gm@..paper}};
\CHToI{geometry}{\tn{Gm@setsize}};
\CHToI{geometry}{\tn{Gm@changelayout}};
\CHToI{fancyhdr}{\tn{f@nch@offset@olh}, \tn{f@nch@offset@orh}, \tn{f@nch@offset@elh}, \tn{f@nch@offset@erh}};
\CHToI{fancyhdr}{\tn{f@nch@offset@olf}, \tn{f@nch@offset@orf}, \tn{f@nch@offset@elf}, \tn{f@nch@offset@erf}};
\CHToM{latex}{\tn{pagestyle}, \tn{thispagestyle}}
\CHToP{latex}{\tn{@outputpage}}
\CHToI{latex}{\tn{ps@\meta{pagestyle}}, \tn{is@specialpage}, \tn{@specialstyle}}
\CHToI{fancyhdr}{\tn{iff@nch@pagestyle@star}};
\CHToI{fancyhdr}{\tn{f@nch@pagestyle}, \tn{iff@nch@footnote}, \tn{f@nch@topfloat}, \tn{f@nch@topfloat}};
\CHToI{fancyhdr}{\tn{f@nch@headinit}, \tn{f@nch@footinit}};
\CHToI{fancyhdr}{\tn{ps@f@nch@fancyproto}, \tn{f@nch@def}, \tn{f@nch@ps@\meta{pagestyle}-is-fancyhdr}};
\CHToI{latex}{\tn{if@fcolmade}};


\section{\CHFrom{module.box}}
\CHToI{latex}{\tn{if@in@minipage@env}};
\CHToI{latex}{\tn{@parboxrestore}, \tn{@mpfn}, \tn{@minipagerestore}, \tn{@setminipage}};
\CHToI{varwidth}{\tn{@vwid@setup}, \tn{@@vwid@minipagerestore}};
\CHToI{latex}{\tn{@iiiparbox}};
\CHToI{latex}{\tn{if@nobreak}};
\CHToI{latex}{\tn{@dischyph}, \tn{if@noskipsec}};
\CHToI{latex}{\tn{@acci}, \tn{@accii}, \tn{@acciii}};
\CHToI{latex}{\tn{@totalleftmargin}};
\CHToI{multicol}{\tn{mult@rightbox}, \tn{mult@nat@firstbox}, \tn{mult@rightbox}};
\CHToP{multicol}{\tn{page@sofar}, \tn{prepare@multicols}, \tn{multi@column@out}};
\CHToH{multicol}{\tn{LR@column@boxes}, \tn{RL@column@boxes}, \tn{mc@align@columns}};
\CHToI{multicol}{\tn{c@minrows}, \tn{c@unbalance}, \tn{c@columnbadness}, \tn{c@finalcolumnbadness}};
\CHToI{rotating}{\tn{Grot@setangle}};
\CHToI{latex}{\tn{color@hbox}};
\CHToI{rotating}{\tn{@rotfloat}, \tn{end@rotfloat}, \tn{@rotdblfloat}, \tn{end@rotdblfloat}};

\section{\CHFrom{module.bgfg}}
\CHToI{latex}{\tn{c@page}};
\CHToI{geometry}{\tn{Gm@layoutwidth}, \tn{Gm@layoutheight}};

\section{\CHFrom{module.struct}}
\CHToI{latex}{\tn{@input}, \tn{@writefile}, \tn{@auxout}};
\CHToI{latex}{\tn{@auxout},\tn{tf@toc}};
\CHToI{latex}{\tn{@kernel@after@enddocument@afterlastpage}};
\CHToI{hyperref}{\tn{Hy@tocdestname}, \tn{Hy@linktoc}, \tn{hyper@link}};
\CHToP{float}{\tn{newfloat}, \tn{floatplacement}};
\CHToP{newfloat}{\tn{@DeclareFloatingEnvironment}, \tn{newfloat@setoptions}};
\CHToP{floatrow}{\tn{DeclareNewFloatType}, \tn{FB@captype}};
\CHToP{tcolorbox}{\tn{tcb@proc@options@init}, \tn{kvtcb@new@listof}, \tn{kvtcb@new@listtype}};
\CHToP{amsthm}{\tn{@xnthm}};
\CHToM{thmtools}{\tn{thmt@mklistcmd}, \tn{listoftheorems}};
\CHToI{thmtools}{\tn{thmtlo@newentry}, \tn{ifthmt@isstarred}, %
  \tn[no-index]{ll@<thmt envname>}, \tn{thmt@thmname}, %
  \tn{thmt@shortoptarg}, \tn{thmtformatoptarg}, \tn{thmt@contentslineShow}};
\CHToI{thmtools}{\tn{thmt@numberline}, \tn{ifthmt@listswap}, \tn{thmt@allenvs}, \tn{thmtlo@newentry}};
\CHToM{thmtools}{\tn{thmt@contentsline}};
\CHToM{hyperref}{\tn{addtocontents}, \tn{contentsline}};
\CHToM{ctexheading}{\tn{CTEX@addloflotskip}};
\CHToI{latex}{\tn{f@size}};
\CHToI{latex}{\tn{c@tocdepth}, \tn{if@restonecol}};
\CHToM{latex}{\tn{@starttoc}};
\CHToI{latex}{\tn{e@alloc@chardef}, \tn{allocationnumber}};
\CHToI{latex}{\tn[no-index]{toclevel@..}};
\CHToP{hyperref}{\tn{H@refstepcounter}};
\CHToP{cleveref}{\tn{refstepcounter@noarg}, \tn{refstepcounter@optarg}};
\CHToI{nameref}{\tn{NR@gettitle}};
\CHToA{hyperref}{\tn{hyper@nopatch@sectioning}};
\CHToA{nameref}{\tn{NR@nopatch@sectioning}};
\CHToI{latex}{\tn{c@secnumdepth}};
\CHToM{latex}{\tn{part}, \tn{chapter}, \tn{section}, \tn{subsection}, \tn{subsubsection}, \tn{paragraph}, \tn{subparagraph}};
\CHToI{latex}{\tn{@topnewpage}};
\CHToI{latex}{\tn{@maketophead}, \tn{@makestophead}, \tn{@afterheading}};
\CHToI{placeins}{\tn{FloatBarrier}};
\CHToI{latex}{\tn{@svsec}, \tn{@svsechd}};
\CHToI{latex}{\tn{@secpenalty}};

% \section{\CHFrom{module.notes}}

\section{\CHFrom{library.index}}
\CHToM{latex}{\tn{@indexfile}};
\CHToA{latex}{\tn[no-index]{@indexfile..}};
\CHToI{latex}{\tn{@idxitem}};
\CHToM{latex}{\tn{index}, \tn{makeindex}, \tn{printindex}};
\CHToI{latex}{\tn{@bsphack}, \tn{@sanitize}, \tn{@wrindex}};
\CHToI{latex}{\tn{@input@}};

\section{\CHFrom{library.box}}
\CHToA{latex}{\tn{@tempdimd}};
\CHToA{longfbox}{\texttt{/longfbox/math}, \texttt{/longfbox/highlight math}};
\CHToI{paracol}{\tn{ifpcol@swapcolumn}, \tn{ifpcol@swapmarginpar}, \tn{ifpcol@bg@swap}};
\CHToI{latex}{\tn{cl@@ckpt}};
\CHToI{paracol}{\tn{pcol@gcounters}, \tn{pcol@globalcounter}};
\CHToI{paracol}{\tn{pcol@colwidthspecleft}, \tn{pcol@colwidthspecright}, \tn{pcol@columnratioleft}, \tn{pcol@columnratioright}};
\CHToI{paracol}{\tn{pcol@mpthreshold@l}, \tn{pcol@mpthreshold@r}};

\section{\CHFrom{library.math}}
\CHToI{latex}{\tn{frozen@everymath}, \tn{frozen@everydisplay}};
\CHToI{amsmath}{\tn{bBigg@}};

\section{\CHFrom{library.counter}}
\CHToI{latex}{\tn{cl@@ckpt}, \tn{@elt}};
\CHToM{latex}{\tn{@stpelt}};

\section{\CHFrom{library.ref}}
\CHToI{latex}{\tn{c@page}, \tn{ifG@refundefined}};

\section{\CHFrom{library.pgf}}
\CHToI{pgf}{\tn{pgf@declareimage}};
\CHToI{tikz}{\tn{tikz@finish}, \tn{tikz@path@do@at@end}};
\CHToI{fancyhdr}{\tn{f@nch@offset@olh}, \tn{f@nch@offset@orh}, \tn{f@nch@offset@elh}, \tn{f@nch@offset@erh}};
\CHToI{fancyhdr}{\tn{f@nch@offset@olf}, \tn{f@nch@offset@orf}, \tn{f@nch@offset@elf}, \tn{f@nch@offset@erf}};

\section{\CHFrom{library.tcb}}
\CHToI{tcolorbox}{\tn{kvtcb@boxsep}};
\CHToI{tcolorbox}{\tn{kvtcb@top@rule@stand}, \tn{kvtcb@bottom@rule@stand}, \tn{kvtcb@top}, \tn{kvtcb@bottom}};
\CHToI{tcolorbox}{\tn{kvtcb@left@rule@stand}, \tn{kvtcb@right@rule@stand}, \tn{kvtcb@leftupper}, \tn{kvtcb@rightupper}};

\section{\CHFrom{library.pdf}}
\CHToI{graphicx}{\tn{Gin@ii}};
\CHToI{newpax}{\tn{NEWPAX@cmd@\meta{type}}, \tn{NEWPAX@skip}, \tn{NEWPAX@includegraphics}, \tn{NEWPAX@AddAnnots}, \tn{NEWPAX@file}, \tn{NEWPAX@Gin@page}};

\section{\pkg{lt3ekeys}、\pkg{lt3ekeyscmd} 和 \pkg{lt3ekeysext}}
\CHToI{ltcmd}{\cs{__cmd_peek_nonspace:NTF}, \cs{__cmd_peek_nonspace_remove:NTF}};
\CHToI{ltcmd}{\cs{__cmd_token_if_cs:NTF}};
\CHToI{l3keys}{\cs{c__keys_type_root_str}, \cs{c__keys_code_root_str}, \cs{c__keys_props_root_str}, \cs{c__keys_inherit_root_str}};
\CHToI{latex}{\tn{e@alloc@top}};
\CHToI{l3keys}{\cs{l__keys_module_str}, \cs{l_keys_path_tl}, \cs{l__keys_inherit_str}};
\CHToM{latex}{\cs{@showcommandlisthook}, \cs{@declarecommandcopylisthook}, \cs{g_hook_patch_action_list_tl}};
\CHToI{lthooks}{\cs{__hook_patch_expand_redefine:NNnn}};

\section{\pkg{lt3ekeys-elkernel}}
\CHToI{latex}{\cs{__kernel_cmd_if_xparse:NTF}};
\CHToI{latex3}{\cs{__kernel_str_to_other_fase:n}};
\CHToI{latex3}{\cs{__kernel_chk_if_free_cs:N}};
\CHToI{latex3}{\cs{__kernel_quark_new_test:N}, \cs{__kernel_quark_new_conditional:Nn}};
\CHToI{latex3}{\cs{__kernel_backend_literal_pdf:e}};
\CHToI{latex3}{\cs{__kernel_file_name_sanitize:n}};

\section{\pkg{lt3ekeys-collectn}}
\CHToI{latex}{\tn{e@alloc@chardef}};

\section{\pkg{updatemarks}}
\CHToI{ltmarks}{\cs{g__mark_classes_seq}};
\CHToI{ltmarks}{\cs{__mark_update_structure_alias:nn}};
\CHToI{ltmarks}{\cs{g__mark_.._.._.._tl}, \cs{c__mark_class_.._mark}};
\CHToP{ltboxes}{\cs{endminipage}};
\CHToP{tcolorbox}{\tn{tcbox@inner@box}, \tn{endtcb@lrbox}, \tn{endtcb@savebox}, \tn{tcb@drawing@env@end}, \tn{tcb@vsplit@upper}, \tn{tcb@vsplit@lower}, \tn{tcb@split@start}, \tn{tcb@split@USL}, \tn{tcb@split@SL@displayed}, \tn{tcb@split@L}};
\CHToP{multicol}{\tn{set@keptmarks}, \tn{endmulticols}, \tn{multi@column@out}, \tn{balance@columns@out}, \tn{balance@columns}, \tn{prep@keptmarks}};
\CHToP{adjmulticol}{\tn{adjmc@process@ne@column}};

\endgroup



\end{document}